diff --git a/api/README.md b/api/README.md index 0a88639041..f25d574dee 100644 --- a/api/README.md +++ b/api/README.md @@ -62,7 +62,7 @@ Returns the current window index | :--- | :--- | :--- | | Not specified | number | | -## [`createWindow(opts)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L243) +## [`createWindow(opts)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L245) Creates a new window and returns an instance of ApplicationWindow. @@ -95,20 +95,22 @@ Creates a new window and returns an instance of ApplicationWindow. | opts.headless | boolean | false | true | whether the window will be headless or not (no frame) | | opts.userScript | string | null | true | A user script that will be injected into the window (desktop only) | | opts.protocolHandlers | string | | true | An array of protocol handler schemes to register with the new window (requires service worker) | +| opts.resourcesDirectory | string | | true | | +| opts.shouldPreferServiceWorker | boolean | false | true | | | Return Value | Type | Description | | :--- | :--- | :--- | | Not specified | Promise | | -### [`radius()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L270) +### [`radius()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L274) -### [`margin()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L275) +### [`margin()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L279) -## [`getScreenSize()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L363) +## [`getScreenSize()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L367) Returns the current screen size. @@ -116,7 +118,7 @@ Returns the current screen size. | :--- | :--- | :--- | | Not specified | Promise<{ width: number, height: number | >} | -## [`getWindows(indices)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L394) +## [`getWindows(indices)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L398) Returns the ApplicationWindow instances for the given indices or all windows if no indices are provided. @@ -128,7 +130,7 @@ Returns the ApplicationWindow instances for the given indices or all windows if | :--- | :--- | :--- | | Not specified | Promise | | -## [`getWindow(index)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L448) +## [`getWindow(index)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L452) Returns the ApplicationWindow instance for the given index @@ -140,7 +142,7 @@ Returns the ApplicationWindow instance for the given index | :--- | :--- | :--- | | Not specified | Promise | the ApplicationWindow instance or null if the window does not exist | -## [`getCurrentWindow()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L458) +## [`getCurrentWindow()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L462) Returns the ApplicationWindow instance for the current window. @@ -148,7 +150,7 @@ Returns the ApplicationWindow instance for the current window. | :--- | :--- | :--- | | Not specified | Promise | | -## [`exit(code)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L467) +## [`exit(code)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L471) Quits the backend process and then quits the render process, the exit code used is the final exit code to the OS. @@ -160,7 +162,7 @@ Quits the backend process and then quits the render process, the exit code used | :--- | :--- | :--- | | Not specified | Promise | | -## [`setSystemMenu(options)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L564) +## [`setSystemMenu(options)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L568) Set the native menu for the app. @@ -255,11 +257,11 @@ Set the native menu for the app. | :--- | :--- | :--- | | Not specified | Promise | | -## [`setTrayMenu()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L571) +## [`setTrayMenu()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L575) An alias to setSystemMenu for creating a tary menu -## [`setSystemMenuItemEnabled(value)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L580) +## [`setSystemMenuItemEnabled(value)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L584) Set the enabled state of the system menu. @@ -271,7 +273,7 @@ Set the enabled state of the system menu. | :--- | :--- | :--- | | Not specified | Promise | | -## [`isPaused()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L588) +## [`isPaused()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L592) Predicate function to determine if application is in a "paused" state. @@ -279,23 +281,23 @@ Predicate function to determine if application is in a "paused" state. | :--- | :--- | :--- | | Not specified | boolean | | -## [runtimeVersion](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L596) +## [runtimeVersion](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L600) Socket Runtime version. -## [debug](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L602) +## [debug](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L606) Runtime debug flag. -## [config](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L608) +## [config](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L612) Application configuration. -## [backend](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L613) +## [backend](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L617) The application's backend instance. -### [`open(opts)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L619) +### [`open(opts)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L623) @@ -308,7 +310,7 @@ The application's backend instance. | :--- | :--- | :--- | | Not specified | Promise | | -### [`close()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L627) +### [`close()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/application.js#L631) @@ -1786,26 +1788,97 @@ Sends an async IPC command request with parameters. This is a `FunctionDeclaration` named `inflateIPCMessageTransfers` in `api/ipc.js`, it's exported but undocumented. -## [`findIPCMessageTransfers()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L1760) +## [`findIPCMessageTransfers()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L1762) This is a `FunctionDeclaration` named `findIPCMessageTransfers` in `api/ipc.js`, it's exported but undocumented. -## [ports](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L1825) +## [ports](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L1821) This is a `VariableDeclaration` named `ports` in `api/ipc.js`, it's exported but undocumented. -## [`IPCMessagePort` (extends `MessagePort`)](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L1827) +## [`IPCMessagePort` (extends `MessagePort`)](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L1823) This is a `ClassDeclaration` named ``IPCMessagePort` (extends `MessagePort`)` in `api/ipc.js`, it's exported but undocumented. -## [`IPCMessageChannel` (extends `MessageChannel`)](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L2064) +## [`IPCMessageChannel` (extends `MessageChannel`)](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L2061) This is a `ClassDeclaration` named ``IPCMessageChannel` (extends `MessageChannel`)` in `api/ipc.js`, it's exported but undocumented. +## [`IPCBroadcastChannel` (extends `EventTarget`)](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L2102) + +This is a `ClassDeclaration` named ``IPCBroadcastChannel` (extends `EventTarget`)` in `api/ipc.js`, it's exported but undocumented. + + +### [undefined](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L2108) + + + +### [undefined](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L2115) + + + +### [undefined](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L2120) + + + +### [undefined](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L2125) + + + +### [undefined](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L2130) + + + +### [undefined](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L2135) + + + +### [undefined](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L2140) + + + +### [`constructor(name, )`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L2146) + + + +| Argument | Type | Default | Optional | Description | +| :--- | :--- | :---: | :---: | :--- | +| name | string | | false | | +| (Position 0) | { origin?: string | } options | false | | + +### [`onmessage()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L2171) + + + +### [`onmessageerror()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L2188) + + + +### [`onerror()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L2205) + + + +### [event](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L2253) + + + +### [`addEventListener(type, callback, , type, callback, )`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/ipc.js#L2286) + + + +| Argument | Type | Default | Optional | Description | +| :--- | :--- | :---: | :---: | :--- | +| type | 'message' | | false | | +| callback | function(MessageEvent):any | | false | | +| (Position 0) | { once?: boolean | } options | false | | +| type | 'messageerror' | | false | | +| callback | function(ErrorEvent):any | | false | | +| (Position 1) | { once?: boolean | } options | false | | + @@ -2198,7 +2271,7 @@ This is a `ClassDeclaration` named ``ProcessEnvironment` (extends `EventTarget`) This is a `VariableDeclaration` named `env` in `api/process.js`, it's exported but undocumented. -## [`nextTick(callback)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/process.js#L202) +## [`nextTick(callback)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/process.js#L204) Adds callback to the 'nextTick' queue. @@ -2206,7 +2279,7 @@ Adds callback to the 'nextTick' queue. | :--- | :--- | :---: | :---: | :--- | | callback | Function | | false | | -## [`hrtime(time)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/process.js#L235) +## [`hrtime(time)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/process.js#L237) Computed high resolution time as a `BigInt`. @@ -2218,7 +2291,7 @@ Computed high resolution time as a `BigInt`. | :--- | :--- | :--- | | Not specified | bigint | | -## [`exit(code)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/process.js#L261) +## [`exit(code)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/process.js#L263) @@ -2226,7 +2299,7 @@ Computed high resolution time as a `BigInt`. | :--- | :--- | :---: | :---: | :--- | | code | number | 0 | true | The exit code. Default: 0. | -## [`memoryUsage()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/process.js#L273) +## [`memoryUsage()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/process.js#L275) Returns an object describing the memory usage of the Node.js process measured in bytes. @@ -2822,11 +2895,43 @@ Get the index of the window -### [`channel()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L92) +### [`channel()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L96) The broadcast channel for this window. -### [`getSize()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L100) +### [`size()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L104) + +Get the size of the window + +| Return Value | Type | Description | +| :--- | :--- | :--- | +| Not specified | { width: number, height: number | } - the size of the window | + +### [`position()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L119) + +get the position of the window + +| Return Value | Type | Description | +| :--- | :--- | :--- | +| Not specified | { x: number, y: number | } - the position of the window | + +### [`title()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L130) + +get the title of the window + +| Return Value | Type | Description | +| :--- | :--- | :--- | +| Not specified | string | the title of the window | + +### [`status()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L138) + +get the status of the window + +| Return Value | Type | Description | +| :--- | :--- | :--- | +| Not specified | string | the status of the window | + +### [`getSize()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L146) Get the size of the window @@ -2834,7 +2939,7 @@ Get the size of the window | :--- | :--- | :--- | | Not specified | { width: number, height: number | } - the size of the window | -### [`getPosition()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L111) +### [`getPosition()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L157) Get the position of the window @@ -2842,7 +2947,7 @@ Get the position of the window | :--- | :--- | :--- | | Not specified | { x: number, y: number | } - the position of the window | -### [`getTitle()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L122) +### [`getTitle()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L168) Get the title of the window @@ -2850,7 +2955,7 @@ Get the title of the window | :--- | :--- | :--- | | Not specified | string | the title of the window | -### [`getStatus()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L130) +### [`getStatus()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L176) Get the status of the window @@ -2858,7 +2963,7 @@ Get the status of the window | :--- | :--- | :--- | | Not specified | string | the status of the window | -### [`close()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L138) +### [`close()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L184) Close the window @@ -2866,7 +2971,7 @@ Close the window | :--- | :--- | :--- | | Not specified | Promise | the options of the window | -### [`show()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L153) +### [`show()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L199) Shows the window @@ -2874,7 +2979,7 @@ Shows the window | :--- | :--- | :--- | | Not specified | Promise | | -### [`hide()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L162) +### [`hide()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L208) Hides the window @@ -2882,7 +2987,7 @@ Hides the window | :--- | :--- | :--- | | Not specified | Promise | | -### [`maximize()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L171) +### [`maximize()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L217) Maximize the window @@ -2890,7 +2995,7 @@ Maximize the window | :--- | :--- | :--- | | Not specified | Promise | | -### [`minimize()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L180) +### [`minimize()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L226) Minimize the window @@ -2898,7 +3003,7 @@ Minimize the window | :--- | :--- | :--- | | Not specified | Promise | | -### [`restore()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L189) +### [`restore()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L235) Restore the window @@ -2906,7 +3011,7 @@ Restore the window | :--- | :--- | :--- | | Not specified | Promise | | -### [`setTitle(title)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L199) +### [`setTitle(title)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L245) Sets the title of the window @@ -2918,7 +3023,7 @@ Sets the title of the window | :--- | :--- | :--- | | Not specified | Promise | | -### [`setSize(opts)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L212) +### [`setSize(opts)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L258) Sets the size of the window @@ -2932,7 +3037,7 @@ Sets the size of the window | :--- | :--- | :--- | | Not specified | Promise | | -### [`setPosition(opts)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L255) +### [`setPosition(opts)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L301) Sets the position of the window @@ -2946,7 +3051,7 @@ Sets the position of the window | :--- | :--- | :--- | | Not specified | Promise | | -### [`navigate(path)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L299) +### [`navigate(path)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L345) Navigate the window to a given path @@ -2958,7 +3063,7 @@ Navigate the window to a given path | :--- | :--- | :--- | | Not specified | Promise | | -### [`showInspector()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L308) +### [`showInspector()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L354) Opens the Web Inspector for the window @@ -2966,7 +3071,7 @@ Opens the Web Inspector for the window | :--- | :--- | :--- | | Not specified | Promise | | -### [`setBackgroundColor(opts)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L325) +### [`setBackgroundColor(opts)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L371) Sets the background color of the window @@ -2982,7 +3087,7 @@ Sets the background color of the window | :--- | :--- | :--- | | Not specified | Promise | | -### [`getBackgroundColor()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L334) +### [`getBackgroundColor()`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L380) Gets the background color of the window @@ -2990,7 +3095,7 @@ Gets the background color of the window | :--- | :--- | :--- | | Not specified | Promise | | -### [`setContextMenu(options)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L343) +### [`setContextMenu(options)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L389) Opens a native context menu. @@ -3002,7 +3107,7 @@ Opens a native context menu. | :--- | :--- | :--- | | Not specified | Promise | | -### [`showOpenFilePicker(options)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L352) +### [`showOpenFilePicker(options)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L398) Shows a native open file dialog. @@ -3014,7 +3119,7 @@ Shows a native open file dialog. | :--- | :--- | :--- | | Not specified | Promise | an array of file paths | -### [`showSaveFilePicker(options)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L370) +### [`showSaveFilePicker(options)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L416) Shows a native save file dialog. @@ -3026,7 +3131,7 @@ Shows a native save file dialog. | :--- | :--- | :--- | | Not specified | Promise | an array of file paths | -### [`showDirectoryFilePicker(options)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L388) +### [`showDirectoryFilePicker(options)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L434) Shows a native directory dialog. @@ -3038,7 +3143,7 @@ Shows a native directory dialog. | :--- | :--- | :--- | | Not specified | Promise | an array of file paths | -### [`send(options)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L413) +### [`send(options)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L459) This is a high-level API that you should use instead of `ipc.request` when you want to send a message to another window or to the backend. @@ -3052,7 +3157,7 @@ This is a high-level API that you should use instead of `ipc.request` when | options.event | string | | false | the event to send | | options.value | string \| object | | true | the value to send | -### [`postMessage(message)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L454) +### [`postMessage(message)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L500) Post a message to a window TODO(@jwerle): research using `BroadcastChannel` instead @@ -3065,7 +3170,7 @@ Post a message to a window | :--- | :--- | :--- | | Not specified | Promise | | -### [`openExternal(value)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L473) +### [`openExternal(value)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L519) Opens an URL in the default application associated with the URL protocol, such as 'https:' for the default web browser. @@ -3078,7 +3183,7 @@ Opens an URL in the default application associated with the URL protocol, | :--- | :--- | :--- | | Not specified | Promise<{ url: string | >} | -### [`revealFile(value)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L488) +### [`revealFile(value)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L534) Opens a file in the default file explorer. @@ -3090,7 +3195,19 @@ Opens a file in the default file explorer. | :--- | :--- | :--- | | Not specified | Promise | | -### [`addListener(event, cb)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L503) +### [`update(title)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L547) + +Updates wnidow state + +| Argument | Type | Default | Optional | Description | +| :--- | :--- | :---: | :---: | :--- | +| title | string | | false | the title of the window | + +| Return Value | Type | Description | +| :--- | :--- | :--- | +| Not specified | Promise | | + +### [`addListener(event, cb)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L559) Adds a listener to the window. @@ -3099,7 +3216,7 @@ Adds a listener to the window. | event | string | | false | the event to listen to | | cb | function(*): void | | false | the callback to call | -### [`on(event, cb)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L521) +### [`on(event, cb)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L577) Adds a listener to the window. An alias for `addListener`. @@ -3108,7 +3225,7 @@ Adds a listener to the window. An alias for `addListener`. | event | string | | false | the event to listen to | | cb | function(*): void | | false | the callback to call | -### [`once(event, cb)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L538) +### [`once(event, cb)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L594) Adds a listener to the window. The listener is removed after the first call. @@ -3117,7 +3234,7 @@ Adds a listener to the window. The listener is removed after the first call. | event | string | | false | the event to listen to | | cb | function(*): void | | false | the callback to call | -### [`removeListener(event, cb)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L554) +### [`removeListener(event, cb)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L610) Removes a listener from the window. @@ -3126,7 +3243,7 @@ Removes a listener from the window. | event | string | | false | the event to remove the listener from | | cb | function(*): void | | false | the callback to remove | -### [`removeAllListeners(event)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L567) +### [`removeAllListeners(event)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L623) Removes all listeners from the window. @@ -3134,7 +3251,7 @@ Removes all listeners from the window. | :--- | :--- | :---: | :---: | :--- | | event | string | | false | the event to remove the listeners from | -### [`off(event, cb)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L583) +### [`off(event, cb)`](https://github.com/socketsupply/socket/blob/v0.6.0-rc.8/api/window.js#L639) Removes a listener from the window. An alias for `removeListener`. diff --git a/api/application.js b/api/application.js index a910c92690..e65dcc03b5 100644 --- a/api/application.js +++ b/api/application.js @@ -238,6 +238,8 @@ export function getCurrentWindowIndex () { * @param {boolean=} [opts.headless=false] - whether the window will be headless or not (no frame) * @param {string=} [opts.userScript=null] - A user script that will be injected into the window (desktop only) * @param {string[]=} [opts.protocolHandlers] - An array of protocol handler schemes to register with the new window (requires service worker) + * @param {string=} [opts.resourcesDirectory] + * @param {boolean=} [opts.shouldPreferServiceWorker=false] * @return {Promise} */ export async function createWindow (opts) { @@ -262,7 +264,9 @@ export async function createWindow (opts) { backgroundColorDark: opts.backgroundColorDark ?? '', backgroundColorLight: opts.backgroundColorLight ?? '', utility: opts.utility ?? false, + resourcesDirectory: opts.resourcesDirectory ?? '', shouldExitApplicationOnClose: opts.shouldExitApplicationOnClose ?? false, + shouldPreferServiceWorker: Boolean(opts.shouldPreferServiceWorker ?? false), /** * @private * @type {number} diff --git a/api/child_process/worker.js b/api/child_process/worker.js index 9fcea0d413..558b9fb532 100644 --- a/api/child_process/worker.js +++ b/api/child_process/worker.js @@ -19,7 +19,7 @@ const propagateWorkerError = err => parentPort.postMessage({ if (process.stdin) { process.stdin.on('data', async (data) => { const { id } = state - const result = await ipc.write('child_process.spawn', { id }, data) + const result = await ipc.write('childProcess.spawn', { id }, data) if (result.err) { propagateWorkerError(result.err) @@ -42,7 +42,7 @@ parentPort.onmessage = async ({ data: { id, method, args } }) => { stderr: opts?.stderr !== false } - const result = await ipc.send('child_process.spawn', params) + const result = await ipc.send('childProcess.spawn', params) if (result.err) { return propagateWorkerError(result.err) @@ -66,25 +66,25 @@ parentPort.onmessage = async ({ data: { id, method, args } }) => { if (!data || BigInt(data.id) !== state.id) return - if (source === 'child_process.spawn' && data.source === 'stdout') { + if (source === 'childProcess.spawn' && data.source === 'stdout') { if (process.stdout) { process.stdout.write(buffer) } } - if (source === 'child_process.spawn' && data.source === 'stderr') { + if (source === 'childProcess.spawn' && data.source === 'stderr') { if (process.stderr) { process.stderr.write(buffer) } } - if (source === 'child_process.spawn' && data.status === 'close') { + if (source === 'childProcess.spawn' && data.status === 'close') { state.exitCode = data.code state.lifecycle = 'close' parentPort.postMessage({ method: 'state', args: [state] }) } - if (source === 'child_process.spawn' && data.status === 'exit') { + if (source === 'childProcess.spawn' && data.status === 'exit') { state.exitCode = data.code state.lifecycle = 'exit' parentPort.postMessage({ method: 'state', args: [state] }) @@ -93,7 +93,7 @@ parentPort.onmessage = async ({ data: { id, method, args } }) => { } if (method === 'kill') { - const result = await ipc.send('child_process.kill', { + const result = await ipc.send('childProcess.kill', { id: state.id, signal: signal.getCode(args[0]) }) diff --git a/api/commonjs/loader.js b/api/commonjs/loader.js index 17f9502b39..69c0723347 100644 --- a/api/commonjs/loader.js +++ b/api/commonjs/loader.js @@ -13,7 +13,8 @@ import URL from '../url.js' import fs from '../fs.js' import os from '../os.js' -const RUNTIME_SERVICE_WORKER_FETCH_MODE = 'Runtime-ServiceWorker-Fetch-Mode' +// TODO(@jwerle): investigate if we really need this header back +// const RUNTIME_SERVICE_WORKER_FETCH_MODE = 'Runtime-ServiceWorker-Fetch-Mode' const RUNTIME_REQUEST_SOURCE_HEADER = 'Runtime-Request-Source' const textDecoder = new TextDecoder() @@ -209,7 +210,9 @@ export class RequestStatus { } if ( + application.config.commonjs_fscheck !== false && os.platform() !== 'android' && + /^(socket:|https:)/.test(this.id) && this.#request.id.includes(`://${application.config.meta_bundle_identifier}`) ) { try { @@ -227,10 +230,6 @@ export class RequestStatus { request.open('HEAD', this.#request.id, false) request.setRequestHeader(RUNTIME_REQUEST_SOURCE_HEADER, 'module') - if (globalThis.isServiceWorkerScope) { - request.setRequestHeader(RUNTIME_SERVICE_WORKER_FETCH_MODE, 'ignore') - } - if (this.#request?.loader) { const entries = this.#request.loader.headers.entries() for (const entry of entries) { @@ -442,6 +441,7 @@ export class Request { } if ( + application.config.commonjs_fscheck !== false && os.platform() !== 'android' && /^(socket:|https:)/.test(this.id) && this.id.includes(`//${application.config.meta_bundle_identifier}/`) @@ -460,10 +460,6 @@ export class Request { request.open('GET', this.id, false) request.setRequestHeader(RUNTIME_REQUEST_SOURCE_HEADER, 'module') - if (globalThis.isServiceWorkerScope) { - request.setRequestHeader(RUNTIME_SERVICE_WORKER_FETCH_MODE, 'ignore') - } - if (typeof options?.responseType === 'string') { request.responseType = options.responseType } diff --git a/api/hooks.js b/api/hooks.js index 4cfe18be83..d2ba98ae02 100644 --- a/api/hooks.js +++ b/api/hooks.js @@ -115,37 +115,39 @@ function dispatchReadyEvent (target) { function proxyGlobalEvents (global, target) { for (const type of GLOBAL_EVENTS) { - const globalObject = GLOBAL_TOP_LEVEL_EVENTS.includes(type) - ? global.top ?? global - : global - - addEventListener(globalObject, type, (event) => { - const { type, data, detail = null, error } = event - const { origin } = location - - if (type === 'applicationurl') { - dispatchEvent(target, new ApplicationURLEvent(type, { - origin, - data: event.data, - url: event.url.toString() - })) - } else if (type === 'error' || error) { - const { message, filename = import.meta.url || globalThis.location.href } = error || {} - dispatchEvent(target, new ErrorEvent(type, { - message, - filename, - error, - detail, - origin - })) - } else if (data || type === 'message') { - dispatchEvent(target, new MessageEvent(type, event)) - } else if (detail) { - dispatchEvent(target, new CustomEvent(type, event)) - } else { - dispatchEvent(target, new Event(type, event)) - } - }) + try { + const globalObject = GLOBAL_TOP_LEVEL_EVENTS.includes(type) + ? global.top ?? global + : global + + addEventListener(globalObject, type, (event) => { + const { type, data, detail = null, error } = event + const { origin } = location + + if (type === 'applicationurl') { + dispatchEvent(target, new ApplicationURLEvent(type, { + origin, + data: event.data, + url: event.url.toString() + })) + } else if (type === 'error' || error) { + const { message, filename = import.meta.url || globalThis.location.href } = error || {} + dispatchEvent(target, new ErrorEvent(type, { + message, + filename, + error, + detail, + origin + })) + } else if (data || type === 'message') { + dispatchEvent(target, new MessageEvent(type, event)) + } else if (detail) { + dispatchEvent(target, new CustomEvent(type, event)) + } else { + dispatchEvent(target, new Event(type, event)) + } + }) + } catch (err) {} } } diff --git a/api/index.d.ts b/api/index.d.ts index a40c9b5ea8..60331e3521 100644 --- a/api/index.d.ts +++ b/api/index.d.ts @@ -7884,6 +7884,8 @@ declare module "socket:application" { * @param {boolean=} [opts.headless=false] - whether the window will be headless or not (no frame) * @param {string=} [opts.userScript=null] - A user script that will be injected into the window (desktop only) * @param {string[]=} [opts.protocolHandlers] - An array of protocol handler schemes to register with the new window (requires service worker) + * @param {string=} [opts.resourcesDirectory] + * @param {boolean=} [opts.shouldPreferServiceWorker=false] * @return {Promise} */ export function createWindow(opts: { @@ -7913,6 +7915,8 @@ declare module "socket:application" { headless?: boolean | undefined; userScript?: string | undefined; protocolHandlers?: string[] | undefined; + resourcesDirectory?: string | undefined; + shouldPreferServiceWorker?: boolean | undefined; }): Promise; /** * Returns the current screen size. @@ -9434,7 +9438,7 @@ declare module "socket:internal/conduit" { * @typedef {{ options: object, payload: Uint8Array }} ReceiveMessage * @typedef {function(Error?, ReceiveCallback | undefined)} ReceiveCallback * @typedef {{ isActive: boolean, handles: { ids: string[], count: number }}} ConduitDiagnostics - * @typedef {{ isActive: boolean, port: number }} ConduitStatus + * @typedef {{ isActive: boolean, port: number, sharedKey: string }} ConduitStatus * @typedef {{ * id?: string|BigInt|number, * sharedKey?: string @@ -9476,6 +9480,17 @@ declare module "socket:internal/conduit" { static waitForActiveState(options?: { maxQueriesForStatus?: number; } | undefined): Promise; + /** + * Gets the current conduit shared key. + * @return {Promise} + */ + static getSharedKey(): Promise; + /** + * Sets the conduit shared key. + * @param {string} sharedKey + * @return {Promise} + */ + static setSharedKey(sharedKey: string): Promise; /** * Creates an instance of Conduit. * @@ -9503,9 +9518,9 @@ declare module "socket:internal/conduit" { */ port: number; /** - * @type {number?} + * @type {string} */ - id: number | null; + id: string; /** * @type {string} */ @@ -9611,6 +9626,7 @@ declare module "socket:internal/conduit" { export type ConduitStatus = { isActive: boolean; port: number; + sharedKey: string; }; export type ConduitOptions = { id?: string | BigInt | number; @@ -10571,7 +10587,6 @@ declare module "socket:internal/database" { delete(key: string, options?: (DatabaseDeleteOptions | undefined) | null): Promise; /** * Gets a "readonly" value by `key` in the `Database` object storage. - * @param {string} key * @param {?DatabaseEntriesOptions|undefined} [options] * @return {Promise} */ diff --git a/api/index.tmp.d.ts b/api/index.tmp.d.ts new file mode 100644 index 0000000000..765844d956 --- /dev/null +++ b/api/index.tmp.d.ts @@ -0,0 +1,18065 @@ +declare module "async/context" { + /** + * @module async.context + * + * Async Context for JavaScript based on the TC39 proposal. + * + * Example usage: + * ```js + * // `AsyncContext` is also globally available as `globalThis.AsyncContext` + * import AsyncContext from 'socket:async/context' + * + * const var = new AsyncContext.Variable() + * var.run('top', () => { + * console.log(var.get()) // 'top' + * queueMicrotask(() => { + * var.run('nested', () => { + * console.log(var.get()) // 'nested' + * }) + * }) + * }) + * ``` + * + * @see {@link https://tc39.es/proposal-async-context} + * @see {@link https://github.com/tc39/proposal-async-context} + */ + /** + * @template T + * @typedef {{ + * name?: string, + * defaultValue?: T + * }} VariableOptions + */ + /** + * @callback AnyFunc + * @template T + * @this T + * @param {...any} args + * @returns {any} + */ + /** + * `FrozenRevert` holds a frozen Mapping that will be simply restored + * when the revert is run. + * @see {@link https://github.com/tc39/proposal-async-context/blob/master/src/fork.ts} + */ + export class FrozenRevert { + /** + * `FrozenRevert` class constructor. + * @param {Mapping} mapping + */ + constructor(mapping: Mapping); + /** + * Restores (unchaged) mapping from this `FrozenRevert`. This function is + * called by `AsyncContext.Storage` when it reverts a current mapping to the + * previous state before a "fork". + * @param {Mapping=} [unused] + * @return {Mapping} + */ + restore(unused?: Mapping | undefined): Mapping; + #private; + } + /** + * Revert holds the state on how to revert a change to the + * `AsyncContext.Storage` current `Mapping` + * @see {@link https://github.com/tc39/proposal-async-context/blob/master/src/fork.ts} + * @template T + */ + export class Revert { + /** + * `Revert` class constructor. + * @param {Mapping} mapping + * @param {Variable} key + */ + constructor(mapping: Mapping, key: Variable); + /** + * @type {T|undefined} + */ + get previousVariable(): T; + /** + * Restores a mapping from this `Revert`. This function is called by + * `AsyncContext.Storage` when it reverts a current mapping to the + * previous state before a "fork". + * @param {Mapping} current + * @return {Mapping} + */ + restore(current: Mapping): Mapping; + #private; + } + /** + * A container for all `AsyncContext.Variable` instances and snapshot state. + * @see {@link https://github.com/tc39/proposal-async-context/blob/master/src/mapping.ts} + */ + export class Mapping { + /** + * `Mapping` class constructor. + * @param {Map, any>} data + */ + constructor(data: Map, any>); + /** + * Freezes the `Mapping` preventing `AsyncContext.Variable` modifications with + * `set()` and `delete()`. + */ + freeze(): void; + /** + * Returns `true` if the `Mapping` is frozen, otherwise `false`. + * @return {boolean} + */ + isFrozen(): boolean; + /** + * Optionally returns a new `Mapping` if the current one is "frozen", + * otherwise it just returns the current instance. + * @return {Mapping} + */ + fork(): Mapping; + /** + * Returns `true` if the `Mapping` has a `AsyncContext.Variable` at `key`, + * otherwise `false. + * @template T + * @param {Variable} key + * @return {boolean} + */ + has(key: Variable): boolean; + /** + * Gets an `AsyncContext.Variable` value at `key`. If not set, this function + * returns `undefined`. + * @template T + * @param {Variable} key + * @return {boolean} + */ + get(key: Variable): boolean; + /** + * Sets an `AsyncContext.Variable` value at `key`. If the `Mapping` is frozen, + * then a "forked" (new) instance with the value set on it is returned, + * otherwise the current instance. + * @template T + * @param {Variable} key + * @param {T} value + * @return {Mapping} + */ + set(key: Variable, value: T): Mapping; + /** + * Delete an `AsyncContext.Variable` value at `key`. + * If the `Mapping` is frozen, then a "forked" (new) instance is returned, + * otherwise the current instance. + * @template T + * @param {Variable} key + * @param {T} value + * @return {Mapping} + */ + delete(key: Variable): Mapping; + #private; + } + /** + * A container of all `AsyncContext.Variable` data. + * @ignore + * @see {@link https://github.com/tc39/proposal-async-context/blob/master/src/storage.ts} + */ + export class Storage { + /** + * The current `Mapping` for this `AsyncContext`. + * @type {Mapping} + */ + static "__#4@#current": Mapping; + /** + * Returns `true` if the current `Mapping` has a + * `AsyncContext.Variable` at `key`, + * otherwise `false. + * @template T + * @param {Variable} key + * @return {boolean} + */ + static has(key: Variable): boolean; + /** + * Gets an `AsyncContext.Variable` value at `key` for the current `Mapping`. + * If not set, this function returns `undefined`. + * @template T + * @param {Variable} key + * @return {T|undefined} + */ + static get(key: Variable): T | undefined; + /** + * Set updates the `AsyncContext.Variable` with a new value and returns a + * revert action that allows the modification to be reversed in the future. + * @template T + * @param {Variable} key + * @param {T} value + * @return {Revert|FrozenRevert} + */ + static set(key: Variable, value: T): Revert | FrozenRevert; + /** + * "Freezes" the current storage `Mapping`, and returns a new `FrozenRevert` + * or `Revert` which can restore the storage state to the state at + * the time of the snapshot. + * @return {FrozenRevert} + */ + static snapshot(): FrozenRevert; + /** + * Restores the storage `Mapping` state to state at the time the + * "revert" (`FrozenRevert` or `Revert`) was created. + * @template T + * @param {Revert|FrozenRevert} revert + */ + static restore(revert: Revert | FrozenRevert): void; + /** + * Switches storage `Mapping` state to the state at the time of a + * "snapshot". + * @param {FrozenRevert} snapshot + * @return {FrozenRevert} + */ + static switch(snapshot: FrozenRevert): FrozenRevert; + } + /** + * `AsyncContext.Variable` is a container for a value that is associated with + * the current execution flow. The value is propagated through async execution + * flows, and can be snapshot and restored with Snapshot. + * @template T + * @see {@link https://github.com/tc39/proposal-async-context/blob/master/README.md#asynccontextvariable} + */ + export class Variable { + /** + * `Variable` class constructor. + * @param {VariableOptions=} [options] + */ + constructor(options?: VariableOptions | undefined); + set defaultValue(defaultValue: T); + /** + * @ignore + */ + get defaultValue(): T; + /** + * @ignore + */ + get revert(): FrozenRevert | Revert; + /** + * The name of this async context variable. + * @type {string} + */ + get name(): string; + /** + * Executes a function `fn` with specified arguments, + * setting a new value to the current context before the call, + * and ensuring the environment is reverted back afterwards. + * The function allows for the modification of a specific context's + * state in a controlled manner, ensuring that any changes can be undone. + * @template T, F extends AnyFunc + * @param {T} value + * @param {F} fn + * @param {...Parameters} args + * @returns {ReturnType} + */ + run(value: T_1, fn: F, ...args: Parameters[]): ReturnType; + /** + * Get the `AsyncContext.Variable` value. + * @template T + * @return {T|undefined} + */ + get(): T_1 | undefined; + #private; + } + /** + * `AsyncContext.Snapshot` allows you to opaquely capture the current values of + * all `AsyncContext.Variable` instances and execute a function at a later time + * as if those values were still the current values (a snapshot and restore). + * @see {@link https://github.com/tc39/proposal-async-context/blob/master/README.md#asynccontextsnapshot} + */ + export class Snapshot { + /** + * Wraps a given function `fn` with additional logic to take a snapshot of + * `Storage` before invoking `fn`. Returns a new function with the same + * signature as `fn` that when called, will invoke `fn` with the current + * `this` context and provided arguments, after restoring the `Storage` + * snapshot. + * + * `AsyncContext.Snapshot.wrap` is a helper which captures the current values + * of all Variables and returns a wrapped function. When invoked, this + * wrapped function restores the state of all Variables and executes the + * inner function. + * + * @see {@link https://github.com/tc39/proposal-async-context/blob/master/README.md#asynccontextsnapshotwrap} + * + * @template F + * @param {F} fn + * @returns {F} + */ + static wrap(fn: F): F; + /** + * Runs the given function `fn` with arguments `args`, using a `null` + * context and the current snapshot. + * + * @template F extends AnyFunc + * @param {F} fn + * @param {...Parameters} args + * @returns {ReturnType} + */ + run(fn: F, ...args: Parameters[]): ReturnType; + #private; + } + /** + * `AsyncContext` container. + */ + export class AsyncContext { + /** + * `AsyncContext.Variable` is a container for a value that is associated with + * the current execution flow. The value is propagated through async execution + * flows, and can be snapshot and restored with Snapshot. + * @see {@link https://github.com/tc39/proposal-async-context/blob/master/README.md#asynccontextvariable} + * @type {typeof Variable} + */ + static Variable: typeof Variable; + /** + * `AsyncContext.Snapshot` allows you to opaquely capture the current values of + * all `AsyncContext.Variable` instances and execute a function at a later time + * as if those values were still the current values (a snapshot and restore). + * @see {@link https://github.com/tc39/proposal-async-context/blob/master/README.md#asynccontextsnapshot} + * @type {typeof Snapshot} + */ + static Snapshot: typeof Snapshot; + } + export default AsyncContext; + export type VariableOptions = { + name?: string; + defaultValue?: T; + }; + export type AnyFunc = () => any; +} +declare module "events" { + export const Event: { + new (type: string, eventInitDict?: EventInit): Event; + prototype: Event; + readonly NONE: 0; + readonly CAPTURING_PHASE: 1; + readonly AT_TARGET: 2; + readonly BUBBLING_PHASE: 3; + } | { + new (): {}; + }; + export const EventTarget: { + new (): {}; + }; + export const CustomEvent: { + new (type: string, eventInitDict?: CustomEventInit): CustomEvent; + prototype: CustomEvent; + } | { + new (type: any, options: any): { + "__#7@#detail": any; + readonly detail: any; + }; + }; + export const MessageEvent: { + new (type: string, eventInitDict?: MessageEventInit): MessageEvent; + prototype: MessageEvent; + } | { + new (type: any, options: any): { + "__#8@#detail": any; + "__#8@#data": any; + readonly detail: any; + readonly data: any; + }; + }; + export const ErrorEvent: { + new (type: string, eventInitDict?: ErrorEventInit): ErrorEvent; + prototype: ErrorEvent; + } | { + new (type: any, options: any): { + "__#9@#detail": any; + "__#9@#error": any; + readonly detail: any; + readonly error: any; + }; + }; + export default EventEmitter; + export function EventEmitter(): void; + export class EventEmitter { + _events: any; + _contexts: any; + _eventsCount: number; + _maxListeners: number; + setMaxListeners(n: any): this; + getMaxListeners(): any; + emit(type: any, ...args: any[]): boolean; + addListener(type: any, listener: any): any; + on(arg0: any, arg1: any): any; + prependListener(type: any, listener: any): any; + once(type: any, listener: any): this; + prependOnceListener(type: any, listener: any): this; + removeListener(type: any, listener: any): this; + off(type: any, listener: any): this; + removeAllListeners(type: any, ...args: any[]): this; + listeners(type: any): any[]; + rawListeners(type: any): any[]; + listenerCount(type: any): any; + eventNames(): (string | symbol)[]; + } + export namespace EventEmitter { + export { EventEmitter }; + export let defaultMaxListeners: number; + export function init(): void; + export function listenerCount(emitter: any, type: any): any; + export { once }; + } + export function once(emitter: any, name: any): Promise; +} +declare module "url/urlpattern/urlpattern" { + export { me as URLPattern }; + var me: { + new (t: {}, r: any, n: any): { + "__#11@#i": any; + "__#11@#n": {}; + "__#11@#t": {}; + "__#11@#e": {}; + "__#11@#s": {}; + "__#11@#l": boolean; + test(t: {}, r: any): boolean; + exec(t: {}, r: any): { + inputs: any[] | {}[]; + }; + readonly protocol: any; + readonly username: any; + readonly password: any; + readonly hostname: any; + readonly port: any; + readonly pathname: any; + readonly search: any; + readonly hash: any; + readonly hasRegExpGroups: boolean; + }; + compareComponent(t: any, r: any, n: any): number; + }; +} +declare module "url/url/url" { + const _default: any; + export default _default; +} +declare module "buffer" { + export default Buffer; + export const File: { + new (fileBits: BlobPart[], fileName: string, options?: FilePropertyBag): File; + prototype: File; + }; + export const Blob: { + new (blobParts?: BlobPart[], options?: BlobPropertyBag): Blob; + prototype: Blob; + }; + export namespace constants { + export { kMaxLength as MAX_LENGTH }; + export { kMaxLength as MAX_STRING_LENGTH }; + } + export const btoa: any; + export const atob: any; + /** + * The Buffer constructor returns instances of `Uint8Array` that have their + * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of + * `Uint8Array`, so the returned instances will have all the node `Buffer` methods + * and the `Uint8Array` methods. Square bracket notation works as expected -- it + * returns a single octet. + * + * The `Uint8Array` prototype remains unmodified. + */ + /** + * @name Buffer + * @extends {Uint8Array} + */ + export function Buffer(arg: any, encodingOrOffset: any, length: any): any; + export class Buffer { + /** + * The Buffer constructor returns instances of `Uint8Array` that have their + * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of + * `Uint8Array`, so the returned instances will have all the node `Buffer` methods + * and the `Uint8Array` methods. Square bracket notation works as expected -- it + * returns a single octet. + * + * The `Uint8Array` prototype remains unmodified. + */ + /** + * @name Buffer + * @extends {Uint8Array} + */ + constructor(arg: any, encodingOrOffset: any, length: any); + get parent(): any; + get offset(): any; + _isBuffer: boolean; + swap16(): this; + swap32(): this; + swap64(): this; + toString(...args: any[]): any; + toLocaleString: any; + equals(b: any): boolean; + inspect(): string; + compare(target: any, start: any, end: any, thisStart: any, thisEnd: any): 0 | 1 | -1; + includes(val: any, byteOffset: any, encoding: any): boolean; + indexOf(val: any, byteOffset: any, encoding: any): any; + lastIndexOf(val: any, byteOffset: any, encoding: any): any; + write(string: any, offset: any, length: any, encoding: any): number; + toJSON(): { + type: string; + data: any; + }; + slice(start: any, end: any): any; + readUintLE: (offset: any, byteLength: any, noAssert: any) => any; + readUIntLE(offset: any, byteLength: any, noAssert: any): any; + readUintBE: (offset: any, byteLength: any, noAssert: any) => any; + readUIntBE(offset: any, byteLength: any, noAssert: any): any; + readUint8: (offset: any, noAssert: any) => any; + readUInt8(offset: any, noAssert: any): any; + readUint16LE: (offset: any, noAssert: any) => number; + readUInt16LE(offset: any, noAssert: any): number; + readUint16BE: (offset: any, noAssert: any) => number; + readUInt16BE(offset: any, noAssert: any): number; + readUint32LE: (offset: any, noAssert: any) => number; + readUInt32LE(offset: any, noAssert: any): number; + readUint32BE: (offset: any, noAssert: any) => number; + readUInt32BE(offset: any, noAssert: any): number; + readBigUInt64LE: any; + readBigUInt64BE: any; + readIntLE(offset: any, byteLength: any, noAssert: any): any; + readIntBE(offset: any, byteLength: any, noAssert: any): any; + readInt8(offset: any, noAssert: any): any; + readInt16LE(offset: any, noAssert: any): number; + readInt16BE(offset: any, noAssert: any): number; + readInt32LE(offset: any, noAssert: any): number; + readInt32BE(offset: any, noAssert: any): number; + readBigInt64LE: any; + readBigInt64BE: any; + readFloatLE(offset: any, noAssert: any): number; + readFloatBE(offset: any, noAssert: any): number; + readDoubleLE(offset: any, noAssert: any): number; + readDoubleBE(offset: any, noAssert: any): number; + writeUintLE: (value: any, offset: any, byteLength: any, noAssert: any) => any; + writeUIntLE(value: any, offset: any, byteLength: any, noAssert: any): any; + writeUintBE: (value: any, offset: any, byteLength: any, noAssert: any) => any; + writeUIntBE(value: any, offset: any, byteLength: any, noAssert: any): any; + writeUint8: (value: any, offset: any, noAssert: any) => any; + writeUInt8(value: any, offset: any, noAssert: any): any; + writeUint16LE: (value: any, offset: any, noAssert: any) => any; + writeUInt16LE(value: any, offset: any, noAssert: any): any; + writeUint16BE: (value: any, offset: any, noAssert: any) => any; + writeUInt16BE(value: any, offset: any, noAssert: any): any; + writeUint32LE: (value: any, offset: any, noAssert: any) => any; + writeUInt32LE(value: any, offset: any, noAssert: any): any; + writeUint32BE: (value: any, offset: any, noAssert: any) => any; + writeUInt32BE(value: any, offset: any, noAssert: any): any; + writeBigUInt64LE: any; + writeBigUInt64BE: any; + writeIntLE(value: any, offset: any, byteLength: any, noAssert: any): any; + writeIntBE(value: any, offset: any, byteLength: any, noAssert: any): any; + writeInt8(value: any, offset: any, noAssert: any): any; + writeInt16LE(value: any, offset: any, noAssert: any): any; + writeInt16BE(value: any, offset: any, noAssert: any): any; + writeInt32LE(value: any, offset: any, noAssert: any): any; + writeInt32BE(value: any, offset: any, noAssert: any): any; + writeBigInt64LE: any; + writeBigInt64BE: any; + writeFloatLE(value: any, offset: any, noAssert: any): any; + writeFloatBE(value: any, offset: any, noAssert: any): any; + writeDoubleLE(value: any, offset: any, noAssert: any): any; + writeDoubleBE(value: any, offset: any, noAssert: any): any; + copy(target: any, targetStart: any, start: any, end: any): number; + fill(val: any, start: any, end: any, encoding: any): this; + } + export namespace Buffer { + export let TYPED_ARRAY_SUPPORT: boolean; + export let poolSize: number; + /** + * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError + * if value is a number. + * Buffer.from(str[, encoding]) + * Buffer.from(array) + * Buffer.from(buffer) + * Buffer.from(arrayBuffer[, byteOffset[, length]]) + **/ + export function from(value: any, encodingOrOffset?: any, length?: any): any; + /** + * Creates a new filled Buffer instance. + * alloc(size[, fill[, encoding]]) + **/ + export function alloc(size: any, fill: any, encoding: any): Uint8Array; + /** + * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance. + * */ + export function allocUnsafe(size: any): Uint8Array; + /** + * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance. + */ + export function allocUnsafeSlow(size: any): Uint8Array; + export function isBuffer(b: any): boolean; + export function compare(a: any, b: any): 0 | 1 | -1; + export function isEncoding(encoding: any): boolean; + export function concat(list: any, length?: any): Uint8Array; + export { byteLength }; + } + export const kMaxLength: 2147483647; + export function SlowBuffer(length: any): Uint8Array; + export const INSPECT_MAX_BYTES: 50; + function byteLength(string: any, encoding: any, ...args: any[]): any; +} +declare module "querystring" { + export function unescapeBuffer(s: any, decodeSpaces: any): any; + export function unescape(s: any, decodeSpaces: any): any; + export function escape(str: any): any; + export function stringify(obj: any, sep: any, eq: any, options: any): string; + export function parse(qs: any, sep: any, eq: any, options: any): {}; + export function decode(qs: any, sep: any, eq: any, options: any): {}; + export function encode(obj: any, sep: any, eq: any, options: any): string; + namespace _default { + export { decode }; + export { encode }; + export { parse }; + export { stringify }; + export { escape }; + export { unescape }; + } + export default _default; +} +declare module "url/index" { + export function parse(input: any, options?: any): { + hash: any; + host: any; + hostname: any; + origin: any; + auth: string; + password: any; + pathname: any; + path: any; + port: any; + protocol: any; + search: any; + searchParams: any; + username: any; + [Symbol.toStringTag]: string; + }; + export function resolve(from: any, to: any): any; + export function format(input: any): any; + export function fileURLToPath(url: any): any; + /** + * @type {Set & { handlers: Set }} + */ + export const protocols: Set & { + handlers: Set; + }; + export default URL; + export class URL { + private constructor(); + } + export const URLSearchParams: any; + export const parseURL: any; + import { URLPattern } from "url/urlpattern/urlpattern"; + export { URLPattern }; +} +declare module "url" { + export * from "url/index"; + export default URL; + import URL from "url/index"; +} +declare module "fs/bookmarks" { + /** + * A map of known absolute file paths to file IDs that + * have been granted access outside of the sandbox. + * XXX(@jwerle): this is currently only used on linux, but valaues may + * be added for all platforms, likely from a file system picker dialog. + * @type {Map} + */ + export const temporary: Map; + namespace _default { + export { temporary }; + } + export default _default; +} +declare module "internal/serialize" { + export default function serialize(value: any): any; +} +declare module "location" { + export class Location { + get url(): URL; + get protocol(): string; + get host(): string; + get hostname(): string; + get port(): string; + get pathname(): string; + get search(): string; + get origin(): string; + get href(): string; + get hash(): string; + toString(): string; + } + const _default: Location; + export default _default; +} +declare module "internal/symbols" { + export const dispose: any; + export const serialize: any; + namespace _default { + export { dispose }; + export { serialize }; + } + export default _default; +} +declare module "gc" { + /** + * Track `object` ref to call `Symbol.for('socket.runtime.gc.finalize')` method when + * environment garbage collects object. + * @param {object} object + * @return {boolean} + */ + export function ref(object: object, ...args: any[]): boolean; + /** + * Stop tracking `object` ref to call `Symbol.for('socket.runtime.gc.finalize')` method when + * environment garbage collects object. + * @param {object} object + * @return {boolean} + */ + export function unref(object: object): boolean; + /** + * An alias for `unref()` + * @param {object} object} + * @return {boolean} + */ + export function retain(object: object): boolean; + /** + * Call finalize on `object` for `gc.finalizer` implementation. + * @param {object} object] + * @return {Promise} + */ + export function finalize(object: object, ...args: any[]): Promise; + /** + * Calls all pending finalization handlers forcefully. This function + * may have unintended consequences as objects be considered finalized + * and still strongly held (retained) somewhere. + */ + export function release(): Promise; + export const finalizers: WeakMap; + export const kFinalizer: unique symbol; + export const finalizer: symbol; + /** + * @type {Set} + */ + export const pool: Set>; + /** + * Static registry for objects to clean up underlying resources when they + * are gc'd by the environment. There is no guarantee that the `finalizer()` + * is called at any time. + */ + export const registry: FinalizationRegistry; + /** + * Default exports which also acts a retained value to persist bound + * `Finalizer#handle()` functions from being gc'd before the + * `FinalizationRegistry` callback is called because `heldValue` must be + * strongly held (retained) in order for the callback to be called. + */ + export const gc: any; + export default gc; + /** + * A container for strongly (retain) referenced finalizer function + * with arguments weakly referenced to an object that will be + * garbage collected. + */ + export class Finalizer { + /** + * Creates a `Finalizer` from input. + */ + static from(handler: any): Finalizer; + /** + * `Finalizer` class constructor. + * @private + * @param {array} args + * @param {function} handle + */ + private constructor(); + args: any[]; + handle: any; + } +} +declare module "ipc" { + export function maybeMakeError(error: any, caller: any): any; + /** + * Parses `seq` as integer value + * @param {string|number} seq + * @param {object=} [options] + * @param {boolean} [options.bigint = false] + * @ignore + */ + export function parseSeq(seq: string | number, options?: object | undefined): number | bigint; + /** + * If `debug.enabled === true`, then debug output will be printed to console. + * @param {(boolean)} [enable] + * @return {boolean} + * @ignore + */ + export function debug(enable?: (boolean)): boolean; + export namespace debug { + let enabled: boolean; + function log(...args: any[]): any; + } + /** + * Find transfers for an in worker global `postMessage` + * that is proxied to the main thread. + * @ignore + */ + export function findMessageTransfers(transfers: any, object: any): any; + /** + * @ignore + */ + export function postMessage(message: any, ...args: any[]): any; + /** + * Waits for the native IPC layer to be ready and exposed on the + * global window object. + * @ignore + */ + export function ready(): Promise; + /** + * Sends a synchronous IPC command over XHR returning a `Result` + * upon success or error. + * @param {string} command + * @param {any?} [value] + * @param {object?} [options] + * @return {Result} + * @ignore + */ + export function sendSync(command: string, value?: any | null, options?: object | null, buffer?: any): Result; + /** + * Emit event to be dispatched on `window` object. + * @param {string} name + * @param {any} value + * @param {EventTarget=} [target = window] + * @param {Object=} options + */ + export function emit(name: string, value: any, target?: EventTarget | undefined, options?: any | undefined): Promise; + /** + * Resolves a request by `seq` with possible value. + * @param {string} seq + * @param {any} value + * @ignore + */ + export function resolve(seq: string, value: any): Promise; + /** + * Sends an async IPC command request with parameters. + * @param {string} command + * @param {any=} value + * @param {object=} [options] + * @param {boolean=} [options.cache=false] + * @param {boolean=} [options.bytes=false] + * @return {Promise} + */ + export function send(command: string, value?: any | undefined, options?: object | undefined): Promise; + /** + * Sends an async IPC command request with parameters and buffered bytes. + * @param {string} command + * @param {any=} value + * @param {(Buffer|Uint8Array|ArrayBuffer|string|Array)=} buffer + * @param {object=} options + * @ignore + */ + export function write(command: string, value?: any | undefined, buffer?: (Buffer | Uint8Array | ArrayBuffer | string | any[]) | undefined, options?: object | undefined): Promise; + /** + * Sends an async IPC command request with parameters requesting a response + * with buffered bytes. + * @param {string} command + * @param {any=} value + * @param {{ timeout?: number, responseType?: string, signal?: AbortSignal, cache?: boolean}=} [options] + * @ignore + */ + export function request(command: string, value?: any | undefined, options?: { + timeout?: number; + responseType?: string; + signal?: AbortSignal; + cache?: boolean; + } | undefined): Promise; + /** + * Factory for creating a proxy based IPC API. + * @param {string} domain + * @param {(function|object)=} ctx + * @param {string=} [ctx.default] + * @return {Proxy} + * @ignore + */ + export function createBinding(domain: string, ctx?: (Function | object) | undefined): ProxyConstructor; + export function inflateIPCMessageTransfers(object: any, types?: Map): any; + export function findIPCMessageTransfers(transfers: any, object: any): any; + /** + * Represents an OK IPC status. + * @ignore + */ + export const OK: 0; + /** + * Represents an ERROR IPC status. + * @ignore + */ + export const ERROR: 1; + /** + * Timeout in milliseconds for IPC requests. + * @ignore + */ + export const TIMEOUT: number; + /** + * Symbol for the `ipc.debug.enabled` property + * @ignore + */ + export const kDebugEnabled: unique symbol; + /** + * @ignore + */ + export class Headers extends globalThis.Headers { + /** + * @ignore + */ + static from(input: any): Headers; + /** + * @ignore + */ + get length(): number; + /** + * @ignore + */ + toJSON(): { + [k: string]: string; + }; + } + const Message_base: any; + /** + * A container for a IPC message based on a `ipc://` URI scheme. + * @ignore + */ + export class Message extends Message_base { + [x: string]: any; + /** + * The expected protocol for an IPC message. + * @ignore + */ + static get PROTOCOL(): string; + /** + * Creates a `Message` instance from a variety of input. + * @param {string|URL|Message|Buffer|object} input + * @param {(object|string|URLSearchParams)=} [params] + * @param {(ArrayBuffer|Uint8Array|string)?} [bytes] + * @return {Message} + * @ignore + */ + static from(input: string | URL | Message | Buffer | object, params?: (object | string | URLSearchParams) | undefined, bytes?: (ArrayBuffer | Uint8Array | string) | null): Message; + /** + * Predicate to determine if `input` is valid for constructing + * a new `Message` instance. + * @param {string|URL|Message|Buffer|object} input + * @return {boolean} + * @ignore + */ + static isValidInput(input: string | URL | Message | Buffer | object): boolean; + /** + * `Message` class constructor. + * @protected + * @param {string|URL} input + * @param {(object|Uint8Array)?} [bytes] + * @ignore + */ + protected constructor(); + /** + * @type {Uint8Array?} + * @ignore + */ + bytes: Uint8Array | null; + /** + * Computed IPC message name. + * @type {string} + * @ignore + */ + get command(): string; + /** + * Computed IPC message name. + * @type {string} + * @ignore + */ + get name(): string; + /** + * Computed `id` value for the command. + * @type {string} + * @ignore + */ + get id(): string; + /** + * Computed `seq` (sequence) value for the command. + * @type {string} + * @ignore + */ + get seq(): string; + /** + * Computed message value potentially given in message parameters. + * This value is automatically decoded, but not treated as JSON. + * @type {string} + * @ignore + */ + get value(): string; + /** + * Computed `index` value for the command potentially referring to + * the window index the command is scoped to or originating from. If not + * specified in the message parameters, then this value defaults to `-1`. + * @type {number} + * @ignore + */ + get index(): number; + /** + * Computed value parsed as JSON. This value is `null` if the value is not present + * or it is invalid JSON. + * @type {object?} + * @ignore + */ + get json(): any; + /** + * Computed readonly object of message parameters. + * @type {object} + * @ignore + */ + get params(): any; + /** + * Gets unparsed message parameters. + * @type {Array>} + * @ignore + */ + get rawParams(): string[][]; + /** + * Returns computed parameters as entries + * @return {Array>} + * @ignore + */ + entries(): Array>; + /** + * Set a parameter `value` by `key`. + * @param {string} key + * @param {any} value + * @ignore + */ + set(key: string, value: any): any; + /** + * Get a parameter value by `key`. + * @param {string} key + * @param {any=} [defaultValue] + * @return {any} + * @ignore + */ + get(key: string, defaultValue?: any | undefined): any; + /** + * Delete a parameter by `key`. + * @param {string} key + * @return {boolean} + * @ignore + */ + delete(key: string): boolean; + /** + * Computed parameter keys. + * @return {Array} + * @ignore + */ + keys(): Array; + /** + * Computed parameter values. + * @return {Array} + * @ignore + */ + values(): Array; + /** + * Predicate to determine if parameter `key` is present in parameters. + * @param {string} key + * @return {boolean} + * @ignore + */ + has(key: string): boolean; + } + /** + * A result type used internally for handling + * IPC result values from the native layer that are in the form + * of `{ err?, data? }`. The `data` and `err` properties on this + * type of object are in tuple form and be accessed at `[data?,err?]` + * @ignore + */ + export class Result { + /** + * Creates a `Result` instance from input that may be an object + * like `{ err?, data? }`, an `Error` instance, or just `data`. + * @param {(object|Error|any)?} result + * @param {Error|object} [maybeError] + * @param {string} [maybeSource] + * @param {object|string|Headers} [maybeHeaders] + * @return {Result} + * @ignore + */ + static from(result: (object | Error | any) | null, maybeError?: Error | object, maybeSource?: string, maybeHeaders?: object | string | Headers): Result; + /** + * `Result` class constructor. + * @private + * @param {string?} [id = null] + * @param {Error?} [err = null] + * @param {object?} [data = null] + * @param {string?} [source = null] + * @param {(object|string|Headers)?} [headers = null] + * @ignore + */ + private constructor(); + /** + * The unique ID for this result. + * @type {string} + * @ignore + */ + id: string; + /** + * An optional error in the result. + * @type {Error?} + * @ignore + */ + err: Error | null; + /** + * Result data if given. + * @type {(string|object|Uint8Array)?} + * @ignore + */ + data: (string | object | Uint8Array) | null; + /** + * The source of this result. + * @type {string?} + * @ignore + */ + source: string | null; + /** + * Result headers, if given. + * @type {Headers?} + * @ignore + */ + headers: Headers | null; + /** + * Computed result length. + * @ignore + */ + get length(): any; + /** + * @ignore + */ + toJSON(): { + headers: { + [k: string]: string; + }; + source: string; + data: any; + err: { + name: string; + message: string; + stack?: string; + cause?: unknown; + type: any; + code: any; + }; + }; + /** + * Generator for an `Iterable` interface over this instance. + * @ignore + */ + [Symbol.iterator](): Generator; + } + export class IPCSearchParams extends URLSearchParams { + constructor(params: any, nonce?: any); + } + /** + * @ignore + */ + export const primordials: any; + export const ports: Map; + export class IPCMessagePort extends MessagePort { + static from(options?: any): any; + static create(options?: any): any; + get id(): any; + get started(): any; + get closed(): any; + set onmessage(onmessage: any); + get onmessage(): any; + set onmessageerror(onmessageerror: any); + get onmessageerror(): any; + close(purge?: boolean): void; + postMessage(message: any, optionsOrTransferList: any): void; + addEventListener(...args: any[]): any; + removeEventListener(...args: any[]): any; + dispatchEvent(event: any): any; + } + export class IPCMessageChannel extends MessageChannel { + constructor(options?: any); + get id(): any; + get port1(): any; + get port2(): any; + get channel(): any; + #private; + } + export class IPCBroadcastChannel extends EventTarget { + static subscriptions: Map; + /** + * @param {string} name + * @param {{ origin?: string }=} [options] + */ + constructor(name: string, options?: { + origin?: string; + } | undefined); + get name(): string; + get origin(): any; + get key(): any; + get token(): string; + set onmessage(onmessage: (arg0: MessageEvent) => any | null); + /** + * @type {function(MessageEvent):any|null} + */ + get onmessage(): (arg0: MessageEvent) => any | null; + set onmessageerror(onmessageerror: (arg0: ErrorEvent) => any | null); + /** + * @type {function(ErrorEvent):any|null} + */ + get onmessageerror(): (arg0: ErrorEvent) => any | null; + set onerror(onerror: (arg0: ErrorEvent) => any | null); + /** + * @type {function(ErrorEvent):any|null} + */ + get onerror(): (arg0: ErrorEvent) => any | null; + startMessages(): Promise; + /** + * @overload + * @param {'message'} type + * @param {function(MessageEvent):any} callback + * @param {{ once?: boolean }=} [options] + * + * @overload + * @param {'messageerror'} type + * @param {function(ErrorEvent):any} callback + * @param {{ once?: boolean }=} [options] + */ + addEventListener(type: "message", callback: (arg0: MessageEvent) => any, options?: { + once?: boolean; + } | undefined): any; + /** + * @overload + * @param {'message'} type + * @param {function(MessageEvent):any} callback + * @param {{ once?: boolean }=} [options] + * + * @overload + * @param {'messageerror'} type + * @param {function(ErrorEvent):any} callback + * @param {{ once?: boolean }=} [options] + */ + addEventListener(type: "messageerror", callback: (arg0: ErrorEvent) => any, options?: { + once?: boolean; + } | undefined): any; + postMessage(message: any, optionsOrTransferList: any): Promise; + #private; + } + export default exports; + import { Buffer } from "buffer"; + import { URL } from "url/index"; + import * as exports from "ipc"; + namespace ___home_werle_repos_socketsupply_socket_api_ipc_ { } +} +declare module "os/constants" { + export type errno = number; + /** + * @typedef {number} errno + * @typedef {number} signal + */ + /** + * A container for all known "errno" constant values. + * Unsupported values have a default value of `0`. + */ + export const errno: any; + export type signal = number; + /** + * A container for all known "signal" constant values. + * Unsupported values have a default value of `0`. + */ + export const signal: any; + namespace _default { + export { errno }; + export { signal }; + } + export default _default; +} +declare module "errno" { + /** + * Converts an `errno` code to its corresponding string message. + * @param {import('./os/constants.js').errno} {code} + * @return {string} + */ + export function toString(code: any): string; + /** + * Gets the code for a given 'errno' name. + * @param {string|number} name + * @return {errno} + */ + export function getCode(name: string | number): errno; + /** + * Gets the name for a given 'errno' code + * @return {string} + * @param {string|number} code + */ + export function getName(code: string | number): string; + /** + * Gets the message for a 'errno' code. + * @param {number|string} code + * @return {string} + */ + export function getMessage(code: number | string): string; + /** + * @typedef {import('./os/constants.js').errno} errno + */ + export const E2BIG: any; + export const EACCES: any; + export const EADDRINUSE: any; + export const EADDRNOTAVAIL: any; + export const EAFNOSUPPORT: any; + export const EAGAIN: any; + export const EALREADY: any; + export const EBADF: any; + export const EBADMSG: any; + export const EBUSY: any; + export const ECANCELED: any; + export const ECHILD: any; + export const ECONNABORTED: any; + export const ECONNREFUSED: any; + export const ECONNRESET: any; + export const EDEADLK: any; + export const EDESTADDRREQ: any; + export const EDOM: any; + export const EDQUOT: any; + export const EEXIST: any; + export const EFAULT: any; + export const EFBIG: any; + export const EHOSTUNREACH: any; + export const EIDRM: any; + export const EILSEQ: any; + export const EINPROGRESS: any; + export const EINTR: any; + export const EINVAL: any; + export const EIO: any; + export const EISCONN: any; + export const EISDIR: any; + export const ELOOP: any; + export const EMFILE: any; + export const EMLINK: any; + export const EMSGSIZE: any; + export const EMULTIHOP: any; + export const ENAMETOOLONG: any; + export const ENETDOWN: any; + export const ENETRESET: any; + export const ENETUNREACH: any; + export const ENFILE: any; + export const ENOBUFS: any; + export const ENODATA: any; + export const ENODEV: any; + export const ENOENT: any; + export const ENOEXEC: any; + export const ENOLCK: any; + export const ENOLINK: any; + export const ENOMEM: any; + export const ENOMSG: any; + export const ENOPROTOOPT: any; + export const ENOSPC: any; + export const ENOSR: any; + export const ENOSTR: any; + export const ENOSYS: any; + export const ENOTCONN: any; + export const ENOTDIR: any; + export const ENOTEMPTY: any; + export const ENOTSOCK: any; + export const ENOTSUP: any; + export const ENOTTY: any; + export const ENXIO: any; + export const EOPNOTSUPP: any; + export const EOVERFLOW: any; + export const EPERM: any; + export const EPIPE: any; + export const EPROTO: any; + export const EPROTONOSUPPORT: any; + export const EPROTOTYPE: any; + export const ERANGE: any; + export const EROFS: any; + export const ESPIPE: any; + export const ESRCH: any; + export const ESTALE: any; + export const ETIME: any; + export const ETIMEDOUT: any; + export const ETXTBSY: any; + export const EWOULDBLOCK: any; + export const EXDEV: any; + export const strings: any; + export { constants }; + namespace _default { + export { constants }; + export { strings }; + export { toString }; + export { getCode }; + export { getMessage }; + } + export default _default; + export type errno = import("os/constants").errno; + import { errno as constants } from "os/constants"; +} +declare module "errors" { + export default exports; + export const ABORT_ERR: any; + export const ENCODING_ERR: any; + export const INVALID_ACCESS_ERR: any; + export const INDEX_SIZE_ERR: any; + export const NETWORK_ERR: any; + export const NOT_ALLOWED_ERR: any; + export const NOT_FOUND_ERR: any; + export const NOT_SUPPORTED_ERR: any; + export const OPERATION_ERR: any; + export const SECURITY_ERR: any; + export const TIMEOUT_ERR: any; + /** + * An `AbortError` is an error type thrown in an `onabort()` level 0 + * event handler on an `AbortSignal` instance. + */ + export class AbortError extends Error { + /** + * The code given to an `ABORT_ERR` `DOMException` + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMException} + */ + static get code(): any; + /** + * `AbortError` class constructor. + * @param {AbortSignal|string} reasonOrSignal + * @param {AbortSignal=} [signal] + */ + constructor(reason: any, signal?: AbortSignal | undefined, ...args: any[]); + signal: AbortSignal; + get name(): string; + get code(): string; + } + /** + * An `BadRequestError` is an error type thrown in an `onabort()` level 0 + * event handler on an `BadRequestSignal` instance. + */ + export class BadRequestError extends Error { + /** + * The default code given to a `BadRequestError` + */ + static get code(): number; + /** + * `BadRequestError` class constructor. + * @param {string} message + * @param {number} [code] + */ + constructor(message: string, ...args: any[]); + get name(): string; + get code(): string; + } + /** + * An `EncodingError` is an error type thrown when an internal exception + * has occurred, such as in the native IPC layer. + */ + export class EncodingError extends Error { + /** + * The code given to an `ENCODING_ERR` `DOMException`. + */ + static get code(): any; + /** + * `EncodingError` class constructor. + * @param {string} message + * @param {number} [code] + */ + constructor(message: string, ...args: any[]); + get name(): string; + get code(): string; + } + /** + * An error type derived from an `errno` code. + */ + export class ErrnoError extends Error { + static get code(): string; + static errno: any; + /** + * `ErrnoError` class constructor. + * @param {import('./errno').errno|string} code + */ + constructor(code: import("errno").errno | string, message?: any, ...args: any[]); + get name(): string; + get code(): number; + #private; + } + /** + * An `FinalizationRegistryCallbackError` is an error type thrown when an internal exception + * has occurred, such as in the native IPC layer. + */ + export class FinalizationRegistryCallbackError extends Error { + /** + * The default code given to an `FinalizationRegistryCallbackError` + */ + static get code(): number; + /** + * `FinalizationRegistryCallbackError` class constructor. + * @param {string} message + * @param {number} [code] + */ + constructor(message: string, ...args: any[]); + get name(): string; + get code(): string; + } + /** + * An `IllegalConstructorError` is an error type thrown when a constructor is + * called for a class constructor when it shouldn't be. + */ + export class IllegalConstructorError extends TypeError { + /** + * The default code given to an `IllegalConstructorError` + */ + static get code(): number; + /** + * `IllegalConstructorError` class constructor. + * @param {string} message + * @param {number} [code] + */ + constructor(message: string, ...args: any[]); + get name(): string; + get code(): string; + } + /** + * An `IndexSizeError` is an error type thrown when an internal exception + * has occurred, such as in the native IPC layer. + */ + export class IndexSizeError extends Error { + /** + * The code given to an `INDEX_SIZE_ERR` `DOMException` + */ + static get code(): any; + /** + * `IndexSizeError` class constructor. + * @param {string} message + * @param {number} [code] + */ + constructor(message: string, ...args: any[]); + get name(): string; + get code(): string; + } + export const kInternalErrorCode: unique symbol; + /** + * An `InternalError` is an error type thrown when an internal exception + * has occurred, such as in the native IPC layer. + */ + export class InternalError extends Error { + /** + * The default code given to an `InternalError` + */ + static get code(): number; + /** + * `InternalError` class constructor. + * @param {string} message + * @param {number} [code] + */ + constructor(message: string, code?: number, ...args: any[]); + get name(): string; + /** + * @param {number|string} + */ + set code(code: string | number); + /** + * @type {number|string} + */ + get code(): string | number; + [exports.kInternalErrorCode]: number; + } + /** + * An `InvalidAccessError` is an error type thrown when an internal exception + * has occurred, such as in the native IPC layer. + */ + export class InvalidAccessError extends Error { + /** + * The code given to an `INVALID_ACCESS_ERR` `DOMException` + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMException} + */ + static get code(): any; + /** + * `InvalidAccessError` class constructor. + * @param {string} message + * @param {number} [code] + */ + constructor(message: string, ...args: any[]); + get name(): string; + get code(): string; + } + /** + * An `NetworkError` is an error type thrown when an internal exception + * has occurred, such as in the native IPC layer. + */ + export class NetworkError extends Error { + /** + * The code given to an `NETWORK_ERR` `DOMException` + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMException} + */ + static get code(): any; + /** + * `NetworkError` class constructor. + * @param {string} message + * @param {number} [code] + */ + constructor(message: string, ...args: any[]); + get name(): string; + get code(): string; + } + /** + * An `NotAllowedError` is an error type thrown when an internal exception + * has occurred, such as in the native IPC layer. + */ + export class NotAllowedError extends Error { + /** + * The code given to an `NOT_ALLOWED_ERR` `DOMException` + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMException} + */ + static get code(): any; + /** + * `NotAllowedError` class constructor. + * @param {string} message + * @param {number} [code] + */ + constructor(message: string, ...args: any[]); + get name(): string; + get code(): string; + } + /** + * An `NotFoundError` is an error type thrown when an internal exception + * has occurred, such as in the native IPC layer. + */ + export class NotFoundError extends Error { + /** + * The code given to an `NOT_FOUND_ERR` `DOMException` + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMException} + */ + static get code(): any; + /** + * `NotFoundError` class constructor. + * @param {string} message + * @param {number} [code] + */ + constructor(message: string, ...args: any[]); + get name(): string; + get code(): string; + } + /** + * An `NotSupportedError` is an error type thrown when an internal exception + * has occurred, such as in the native IPC layer. + */ + export class NotSupportedError extends Error { + /** + * The code given to an `NOT_SUPPORTED_ERR` `DOMException` + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMException} + */ + static get code(): any; + /** + * `NotSupportedError` class constructor. + * @param {string} message + * @param {number} [code] + */ + constructor(message: string, ...args: any[]); + get name(): string; + get code(): string; + } + /** + * An `ModuleNotFoundError` is an error type thrown when an imported or + * required module is not found. + */ + export class ModuleNotFoundError extends exports.NotFoundError { + /** + * `ModuleNotFoundError` class constructor. + * @param {string} message + * @param {string[]=} [requireStack] + */ + constructor(message: string, requireStack?: string[] | undefined); + requireStack: string[]; + } + /** + * An `OperationError` is an error type thrown when an internal exception + * has occurred, such as in the native IPC layer. + */ + export class OperationError extends Error { + /** + * The code given to an `OPERATION_ERR` `DOMException` + */ + static get code(): any; + /** + * `OperationError` class constructor. + * @param {string} message + * @param {number} [code] + */ + constructor(message: string, ...args: any[]); + get name(): string; + get code(): string; + } + /** + * An `SecurityError` is an error type thrown when an internal exception + * has occurred, such as in the native IPC layer. + */ + export class SecurityError extends Error { + /** + * The code given to an `SECURITY_ERR` `DOMException` + */ + static get code(): any; + /** + * `SecurityError` class constructor. + * @param {string} message + * @param {number} [code] + */ + constructor(message: string, ...args: any[]); + get name(): string; + get code(): string; + } + /** + * An `TimeoutError` is an error type thrown when an operation timesout. + */ + export class TimeoutError extends Error { + /** + * The code given to an `TIMEOUT_ERR` `DOMException` + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMException} + */ + static get code(): any; + /** + * `TimeoutError` class constructor. + * @param {string} message + */ + constructor(message: string, ...args: any[]); + get name(): string; + get code(): string; + } + import * as exports from "errors"; + namespace ___home_werle_repos_socketsupply_socket_api_errors_ { } +} +declare module "mime/params" { + export class MIMEParams extends Map { + constructor(); + constructor(entries?: readonly (readonly [any, any])[]); + constructor(); + constructor(iterable?: Iterable); + } + export default MIMEParams; +} +declare module "mime/type" { + export class MIMEType { + constructor(input: any); + set type(value: any); + get type(): any; + set subtype(value: any); + get subtype(): any; + get essence(): string; + get params(): any; + toString(): string; + toJSON(): string; + #private; + } + export default MIMEType; +} +declare module "util/types" { + /** + * Returns `true` if input is a plan `Object` instance. + * @param {any} input + * @return {boolean} + */ + export function isPlainObject(input: any): boolean; + /** + * Returns `true` if input is an `AsyncFunction` + * @param {any} input + * @return {boolean} + */ + export function isAsyncFunction(input: any): boolean; + /** + * Returns `true` if input is an `Function` + * @param {any} input + * @return {boolean} + */ + export function isFunction(input: any): boolean; + /** + * Returns `true` if input is an `AsyncFunction` object. + * @param {any} input + * @return {boolean} + */ + export function isAsyncFunctionObject(input: any): boolean; + /** + * Returns `true` if input is an `Function` object. + * @param {any} input + * @return {boolean} + */ + export function isFunctionObject(input: any): boolean; + /** + * Always returns `false`. + * @param {any} input + * @return {boolean} + */ + export function isExternal(input: any): boolean; + /** + * Returns `true` if input is a `Date` instance. + * @param {any} input + * @return {boolean} + */ + export function isDate(input: any): boolean; + /** + * Returns `true` if input is an `arguments` object. + * @param {any} input + * @return {boolean} + */ + export function isArgumentsObject(input: any): boolean; + /** + * Returns `true` if input is a `BigInt` object. + * @param {any} input + * @return {boolean} + */ + export function isBigIntObject(input: any): boolean; + /** + * Returns `true` if input is a `Boolean` object. + * @param {any} input + * @return {boolean} + */ + export function isBooleanObject(input: any): boolean; + /** + * Returns `true` if input is a `Number` object. + * @param {any} input + * @return {boolean} + */ + export function isNumberObject(input: any): boolean; + /** + * Returns `true` if input is a `String` object. + * @param {any} input + * @return {boolean} + */ + export function isStringObject(input: any): boolean; + /** + * Returns `true` if input is a `Symbol` object. + * @param {any} input + * @return {boolean} + */ + export function isSymbolObject(input: any): boolean; + /** + * Returns `true` if input is native `Error` instance. + * @param {any} input + * @return {boolean} + */ + export function isNativeError(input: any): boolean; + /** + * Returns `true` if input is a `RegExp` instance. + * @param {any} input + * @return {boolean} + */ + export function isRegExp(input: any): boolean; + /** + * Returns `true` if input is a `GeneratorFunction`. + * @param {any} input + * @return {boolean} + */ + export function isGeneratorFunction(input: any): boolean; + /** + * Returns `true` if input is an `AsyncGeneratorFunction`. + * @param {any} input + * @return {boolean} + */ + export function isAsyncGeneratorFunction(input: any): boolean; + /** + * Returns `true` if input is an instance of a `Generator`. + * @param {any} input + * @return {boolean} + */ + export function isGeneratorObject(input: any): boolean; + /** + * Returns `true` if input is a `Promise` instance. + * @param {any} input + * @return {boolean} + */ + export function isPromise(input: any): boolean; + /** + * Returns `true` if input is a `Map` instance. + * @param {any} input + * @return {boolean} + */ + export function isMap(input: any): boolean; + /** + * Returns `true` if input is a `Set` instance. + * @param {any} input + * @return {boolean} + */ + export function isSet(input: any): boolean; + /** + * Returns `true` if input is an instance of an `Iterator`. + * @param {any} input + * @return {boolean} + */ + export function isIterator(input: any): boolean; + /** + * Returns `true` if input is an instance of an `AsyncIterator`. + * @param {any} input + * @return {boolean} + */ + export function isAsyncIterator(input: any): boolean; + /** + * Returns `true` if input is an instance of a `MapIterator`. + * @param {any} input + * @return {boolean} + */ + export function isMapIterator(input: any): boolean; + /** + * Returns `true` if input is an instance of a `SetIterator`. + * @param {any} input + * @return {boolean} + */ + export function isSetIterator(input: any): boolean; + /** + * Returns `true` if input is a `WeakMap` instance. + * @param {any} input + * @return {boolean} + */ + export function isWeakMap(input: any): boolean; + /** + * Returns `true` if input is a `WeakSet` instance. + * @param {any} input + * @return {boolean} + */ + export function isWeakSet(input: any): boolean; + /** + * Returns `true` if input is an `ArrayBuffer` instance. + * @param {any} input + * @return {boolean} + */ + export function isArrayBuffer(input: any): boolean; + /** + * Returns `true` if input is an `DataView` instance. + * @param {any} input + * @return {boolean} + */ + export function isDataView(input: any): boolean; + /** + * Returns `true` if input is a `SharedArrayBuffer`. + * This will always return `false` if a `SharedArrayBuffer` + * type is not available. + * @param {any} input + * @return {boolean} + */ + export function isSharedArrayBuffer(input: any): boolean; + /** + * Not supported. This function will return `false` always. + * @param {any} input + * @return {boolean} + */ + export function isProxy(input: any): boolean; + /** + * Returns `true` if input looks like a module namespace object. + * @param {any} input + * @return {boolean} + */ + export function isModuleNamespaceObject(input: any): boolean; + /** + * Returns `true` if input is an `ArrayBuffer` of `SharedArrayBuffer`. + * @param {any} input + * @return {boolean} + */ + export function isAnyArrayBuffer(input: any): boolean; + /** + * Returns `true` if input is a "boxed" primitive. + * @param {any} input + * @return {boolean} + */ + export function isBoxedPrimitive(input: any): boolean; + /** + * Returns `true` if input is an `ArrayBuffer` view. + * @param {any} input + * @return {boolean} + */ + export function isArrayBufferView(input: any): boolean; + /** + * Returns `true` if input is a `TypedArray` instance. + * @param {any} input + * @return {boolean} + */ + export function isTypedArray(input: any): boolean; + /** + * Returns `true` if input is an `Uint8Array` instance. + * @param {any} input + * @return {boolean} + */ + export function isUint8Array(input: any): boolean; + /** + * Returns `true` if input is an `Uint8ClampedArray` instance. + * @param {any} input + * @return {boolean} + */ + export function isUint8ClampedArray(input: any): boolean; + /** + * Returns `true` if input is an `Uint16Array` instance. + * @param {any} input + * @return {boolean} + */ + export function isUint16Array(input: any): boolean; + /** + * Returns `true` if input is an `Uint32Array` instance. + * @param {any} input + * @return {boolean} + */ + export function isUint32Array(input: any): boolean; + /** + * Returns `true` if input is an Int8Array`` instance. + * @param {any} input + * @return {boolean} + */ + export function isInt8Array(input: any): boolean; + /** + * Returns `true` if input is an `Int16Array` instance. + * @param {any} input + * @return {boolean} + */ + export function isInt16Array(input: any): boolean; + /** + * Returns `true` if input is an `Int32Array` instance. + * @param {any} input + * @return {boolean} + */ + export function isInt32Array(input: any): boolean; + /** + * Returns `true` if input is an `Float32Array` instance. + * @param {any} input + * @return {boolean} + */ + export function isFloat32Array(input: any): boolean; + /** + * Returns `true` if input is an `Float64Array` instance. + * @param {any} input + * @return {boolean} + */ + export function isFloat64Array(input: any): boolean; + /** + * Returns `true` if input is an `BigInt64Array` instance. + * @param {any} input + * @return {boolean} + */ + export function isBigInt64Array(input: any): boolean; + /** + * Returns `true` if input is an `BigUint64Array` instance. + * @param {any} input + * @return {boolean} + */ + export function isBigUint64Array(input: any): boolean; + /** + * @ignore + * @param {any} input + * @return {boolean} + */ + export function isKeyObject(input: any): boolean; + /** + * Returns `true` if input is a `CryptoKey` instance. + * @param {any} input + * @return {boolean} + */ + export function isCryptoKey(input: any): boolean; + /** + * Returns `true` if input is an `Array`. + * @param {any} input + * @return {boolean} + */ + export const isArray: any; + export default exports; + import * as exports from "util/types"; + namespace ___home_werle_repos_socketsupply_socket_api_util_types_ { } +} +declare module "util" { + export function debug(section: any): { + (...args: any[]): void; + enabled: boolean; + }; + export function hasOwnProperty(object: any, property: any): any; + export function isDate(object: any): boolean; + export function isTypedArray(object: any): boolean; + export function isArrayLike(input: any): boolean; + export function isError(object: any): boolean; + export function isSymbol(value: any): value is symbol; + export function isNumber(value: any): boolean; + export function isBoolean(value: any): boolean; + export function isArrayBufferView(buf: any): boolean; + export function isAsyncFunction(object: any): boolean; + export function isArgumentsObject(object: any): boolean; + export function isEmptyObject(object: any): boolean; + export function isObject(object: any): boolean; + export function isUndefined(value: any): boolean; + export function isNull(value: any): boolean; + export function isNullOrUndefined(value: any): boolean; + export function isPrimitive(value: any): boolean; + export function isRegExp(value: any): boolean; + export function isPlainObject(object: any): boolean; + export function isArrayBuffer(object: any): boolean; + export function isBufferLike(object: any): boolean; + export function isFunction(value: any): boolean; + export function isErrorLike(error: any): boolean; + export function isClass(value: any): boolean; + export function isBuffer(value: any): boolean; + export function isPromiseLike(object: any): boolean; + export function toString(object: any): any; + export function toBuffer(object: any, encoding?: any): any; + export function toProperCase(string: any): any; + export function splitBuffer(buffer: any, highWaterMark: any): any[]; + export function clamp(value: any, min: any, max: any): number; + export function promisify(original: any): any; + export function inspect(value: any, options: any): any; + export namespace inspect { + let ignore: symbol; + let custom: symbol; + } + export function format(format: any, ...args: any[]): string; + export function parseJSON(string: any): any; + export function parseHeaders(headers: any): string[][]; + export function noop(): void; + export function isValidPercentageValue(input: any): boolean; + export function compareBuffers(a: any, b: any): any; + export function inherits(Constructor: any, Super: any): void; + /** + * @ignore + * @param {string} source + * @return {boolean} + */ + export function isESMSource(source: string): boolean; + export function deprecate(...args: any[]): void; + export const TextDecoder: { + new (label?: string, options?: TextDecoderOptions): TextDecoder; + prototype: TextDecoder; + }; + export const TextEncoder: { + new (): TextEncoder; + prototype: TextEncoder; + }; + export const isArray: any; + export const inspectSymbols: symbol[]; + export class IllegalConstructor { + } + export const ESM_TEST_REGEX: RegExp; + export default exports; + import types from "util/types"; + import { MIMEType } from "mime/type"; + import { MIMEParams } from "mime/params"; + import * as exports from "util"; + namespace ___home_werle_repos_socketsupply_socket_api_util_ { } + export { types, MIMEType, MIMEParams }; +} +declare module "async/wrap" { + /** + * Returns `true` if a given function `fn` has the "async" wrapped tag, + * meaning it was "tagged" in a `wrap(fn)` call before, otherwise this + * function will return `false`. + * @ignore + * @param {function} fn + * @param {boolean} + */ + export function isTagged(fn: Function): boolean; + /** + * Tags a function `fn` as being "async wrapped" so subsequent calls to + * `wrap(fn)` do not wrap an already wrapped function. + * @ignore + * @param {function} fn + * @return {function} + */ + export function tag(fn: Function): Function; + /** + * Wraps a function `fn` that captures a snapshot of the current async + * context. This function is idempotent and will not wrap a function more + * than once. + * @ignore + * @param {function} fn + * @return {function} + */ + export function wrap(fn: Function): Function; + export const symbol: unique symbol; + export default wrap; +} +declare module "internal/async/hooks" { + export function dispatch(hook: any, asyncId: any, type: any, triggerAsyncId: any, resource: any): void; + export function getNextAsyncResourceId(): number; + export function executionAsyncResource(): any; + export function executionAsyncId(): any; + export function triggerAsyncId(): any; + export function getDefaultExecutionAsyncId(): any; + export function wrap(callback: any, type: any, asyncId?: number, triggerAsyncId?: any, resource?: any): (...args: any[]) => any; + export function getTopLevelAsyncResourceName(): any; + /** + * The default top level async resource ID + * @type {number} + */ + export const TOP_LEVEL_ASYNC_RESOURCE_ID: number; + export namespace state { + let defaultExecutionAsyncId: number; + } + export namespace hooks { + let init: any[]; + let before: any[]; + let after: any[]; + let destroy: any[]; + let promiseResolve: any[]; + } + /** + * A base class for the `AsyncResource` class or other higher level async + * resource classes. + */ + export class CoreAsyncResource { + /** + * `CoreAsyncResource` class constructor. + * @param {string} type + * @param {object|number=} [options] + */ + constructor(type: string, options?: (object | number) | undefined); + /** + * The `CoreAsyncResource` type. + * @type {string} + */ + get type(): string; + /** + * `true` if the `CoreAsyncResource` was destroyed, otherwise `false`. This + * value is only set to `true` if `emitDestroy()` was called, likely from + * destroying the resource manually. + * @type {boolean} + */ + get destroyed(): boolean; + /** + * The unique async resource ID. + * @return {number} + */ + asyncId(): number; + /** + * The trigger async resource ID. + * @return {number} + */ + triggerAsyncId(): number; + /** + * Manually emits destroy hook for the resource. + * @return {CoreAsyncResource} + */ + emitDestroy(): CoreAsyncResource; + /** + * Binds function `fn` with an optional this `thisArg` binding to run + * in the execution context of this `CoreAsyncResource`. + * @param {function} fn + * @param {object=} [thisArg] + * @return {function} + */ + bind(fn: Function, thisArg?: object | undefined): Function; + /** + * Runs function `fn` in the execution context of this `CoreAsyncResource`. + * @param {function} fn + * @param {object=} [thisArg] + * @param {...any} [args] + * @return {any} + */ + runInAsyncScope(fn: Function, thisArg?: object | undefined, ...args?: any[]): any; + #private; + } + export class TopLevelAsyncResource extends CoreAsyncResource { + } + export const asyncContextVariable: Variable; + export const topLevelAsyncResource: TopLevelAsyncResource; + export default hooks; + import { Variable } from "async/context"; +} +declare module "async/resource" { + /** + * @typedef {{ + * triggerAsyncId?: number, + * requireManualDestroy?: boolean + * }} AsyncResourceOptions + */ + /** + * A container that should be extended that represents a resource with + * an asynchronous execution context. + */ + export class AsyncResource extends CoreAsyncResource { + /** + * Binds function `fn` with an optional this `thisArg` binding to run + * in the execution context of an anonymous `AsyncResource`. + * @param {function} fn + * @param {object|string=} [type] + * @param {object=} [thisArg] + * @return {function} + */ + static bind(fn: Function, type?: (object | string) | undefined, thisArg?: object | undefined): Function; + /** + * `AsyncResource` class constructor. + * @param {string} type + * @param {AsyncResourceOptions|number=} [options] + */ + constructor(type: string, options?: (AsyncResourceOptions | number) | undefined); + } + export default AsyncResource; + export type AsyncResourceOptions = { + triggerAsyncId?: number; + requireManualDestroy?: boolean; + }; + import { executionAsyncResource } from "internal/async/hooks"; + import { executionAsyncId } from "internal/async/hooks"; + import { triggerAsyncId } from "internal/async/hooks"; + import { CoreAsyncResource } from "internal/async/hooks"; + export { executionAsyncResource, executionAsyncId, triggerAsyncId }; +} +declare module "async/hooks" { + /** + * Factory for creating a `AsyncHook` instance. + * @param {AsyncHookCallbackOptions|AsyncHookCallbacks=} [callbacks] + * @return {AsyncHook} + */ + export function createHook(callbacks?: (AsyncHookCallbackOptions | AsyncHookCallbacks) | undefined): AsyncHook; + /** + * A container for `AsyncHooks` callbacks. + * @ignore + */ + export class AsyncHookCallbacks { + /** + * `AsyncHookCallbacks` class constructor. + * @ignore + * @param {AsyncHookCallbackOptions} [options] + */ + constructor(options?: AsyncHookCallbackOptions); + init(asyncId: any, type: any, triggerAsyncId: any, resource: any): void; + before(asyncId: any): void; + after(asyncId: any): void; + destroy(asyncId: any): void; + promiseResolve(asyncId: any): void; + } + /** + * A container for registering various callbacks for async resource hooks. + */ + export class AsyncHook { + /** + * @param {AsyncHookCallbackOptions|AsyncHookCallbacks=} [options] + */ + constructor(callbacks?: any); + /** + * @type {boolean} + */ + get enabled(): boolean; + /** + * Enable the async hook. + * @return {AsyncHook} + */ + enable(): AsyncHook; + /** + * Disables the async hook + * @return {AsyncHook} + */ + disable(): AsyncHook; + #private; + } + export default createHook; + import { executionAsyncResource } from "internal/async/hooks"; + import { executionAsyncId } from "internal/async/hooks"; + import { triggerAsyncId } from "internal/async/hooks"; + export { executionAsyncResource, executionAsyncId, triggerAsyncId }; +} +declare module "async/storage" { + /** + * A container for storing values that remain present during + * asynchronous operations. + */ + export class AsyncLocalStorage { + /** + * Binds function `fn` to run in the execution context of an + * anonymous `AsyncResource`. + * @param {function} fn + * @return {function} + */ + static bind(fn: Function): Function; + /** + * Captures the current async context and returns a function that runs + * a function in that execution context. + * @return {function} + */ + static snapshot(): Function; + /** + * @type {boolean} + */ + get enabled(): boolean; + /** + * Disables the `AsyncLocalStorage` instance. When disabled, + * `getStore()` will always return `undefined`. + */ + disable(): void; + /** + * Enables the `AsyncLocalStorage` instance. + */ + enable(): void; + /** + * Enables and sets the `AsyncLocalStorage` instance default store value. + * @param {any} store + */ + enterWith(store: any): void; + /** + * Runs function `fn` in the current asynchronous execution context with + * a given `store` value and arguments given to `fn`. + * @param {any} store + * @param {function} fn + * @param {...any} args + * @return {any} + */ + run(store: any, fn: Function, ...args: any[]): any; + exit(fn: any, ...args: any[]): any; + /** + * If the `AsyncLocalStorage` instance is enabled, it returns the current + * store value for this asynchronous execution context. + * @return {any|undefined} + */ + getStore(): any | undefined; + #private; + } + export default AsyncLocalStorage; +} +declare module "async/deferred" { + /** + * Dispatched when a `Deferred` internal promise is resolved. + */ + export class DeferredResolveEvent extends Event { + /** + * `DeferredResolveEvent` class constructor + * @ignore + * @param {string=} [type] + * @param {any=} [result] + */ + constructor(type?: string | undefined, result?: any | undefined); + /** + * The `Deferred` promise result value. + * @type {any?} + */ + result: any | null; + } + /** + * Dispatched when a `Deferred` internal promise is rejected. + */ + export class DeferredRejectEvent { + /** + * `DeferredRejectEvent` class constructor + * @ignore + * @param {string=} [type] + * @param {Error=} [error] + */ + constructor(type?: string | undefined, error?: Error | undefined); + } + /** + * A utility class for creating deferred promises. + */ + export class Deferred extends EventTarget { + /** + * `Deferred` class constructor. + * @param {Deferred|Promise?} [promise] + */ + constructor(promise?: Deferred | (Promise | null)); + /** + * Function to resolve the associated promise. + * @type {function} + */ + resolve: Function; + /** + * Function to reject the associated promise. + * @type {function} + */ + reject: Function; + /** + * Attaches a fulfillment callback and a rejection callback to the promise, + * and returns a new promise resolving to the return value of the called + * callback. + * @param {function(any)=} [resolve] + * @param {function(Error)=} [reject] + */ + then(resolve?: ((arg0: any) => any) | undefined, reject?: ((arg0: Error) => any) | undefined): Promise; + /** + * Attaches a rejection callback to the promise, and returns a new promise + * resolving to the return value of the callback if it is called, or to its + * original fulfillment value if the promise is instead fulfilled. + * @param {function(Error)=} [callback] + */ + catch(callback?: ((arg0: Error) => any) | undefined): Promise; + /** + * Attaches a callback for when the promise is settled (fulfilled or rejected). + * @param {function(any?)} [callback] + */ + finally(callback?: (arg0: any | null) => any): Promise; + /** + * The promise associated with this Deferred instance. + * @type {Promise} + */ + get promise(): Promise; + /** + * A string representation of this Deferred instance. + * @type {string} + * @ignore + */ + get [Symbol.toStringTag](): string; + #private; + } + export default Deferred; +} +declare module "async" { + export default exports; + import AsyncLocalStorage from "async/storage"; + import AsyncResource from "async/resource"; + import AsyncContext from "async/context"; + import Deferred from "async/deferred"; + import { executionAsyncResource } from "async/hooks"; + import { executionAsyncId } from "async/hooks"; + import { triggerAsyncId } from "async/hooks"; + import { createHook } from "async/hooks"; + import { AsyncHook } from "async/hooks"; + import * as exports from "async"; + namespace ___home_werle_repos_socketsupply_socket_api_async_ { } + export { AsyncLocalStorage, AsyncResource, AsyncContext, Deferred, executionAsyncResource, executionAsyncId, triggerAsyncId, createHook, AsyncHook }; +} +declare module "application/menu" { + /** + * Internal IPC for setting an application menu + * @ignore + */ + export function setMenu(options: any, type: any): Promise; + /** + * Internal IPC for setting an application context menu + * @ignore + */ + export function setContextMenu(options: any): Promise; + /** + * A `Menu` is base class for a `ContextMenu`, `SystemMenu`, or `TrayMenu`. + */ + export class Menu extends EventTarget { + /** + * `Menu` class constructor. + * @ignore + * @param {string} type + */ + constructor(type: string); + /** + * The broadcast channel for this menu. + * @ignore + * @type {BroadcastChannel} + */ + get channel(): BroadcastChannel; + /** + * The `Menu` instance type. + * @type {('context'|'system'|'tray')?} + */ + get type(): "tray" | "system" | "context"; + /** + * Setter for the level 1 'error'` event listener. + * @ignore + * @type {function(ErrorEvent)?} + */ + set onerror(onerror: (arg0: ErrorEvent) => any); + /** + * Level 1 'error'` event listener. + * @type {function(ErrorEvent)?} + */ + get onerror(): (arg0: ErrorEvent) => any; + /** + * Setter for the level 1 'menuitem'` event listener. + * @ignore + * @type {function(MenuItemEvent)?} + */ + set onmenuitem(onmenuitem: (arg0: menuitemEvent) => any); + /** + * Level 1 'menuitem'` event listener. + * @type {function(menuitemEvent)?} + */ + get onmenuitem(): (arg0: menuitemEvent) => any; + /** + * Set the menu layout for this `Menu` instance. + * @param {string|object} layoutOrOptions + * @param {object=} [options] + */ + set(layoutOrOptions: string | object, options?: object | undefined): Promise; + #private; + } + /** + * A container for various `Menu` instances. + */ + export class MenuContainer extends EventTarget { + /** + * `MenuContainer` class constructor. + * @param {EventTarget} [sourceEventTarget] + * @param {object=} [options] + */ + constructor(sourceEventTarget?: EventTarget, options?: object | undefined); + /** + * Setter for the level 1 'error'` event listener. + * @ignore + * @type {function(ErrorEvent)?} + */ + set onerror(onerror: (arg0: ErrorEvent) => any); + /** + * Level 1 'error'` event listener. + * @type {function(ErrorEvent)?} + */ + get onerror(): (arg0: ErrorEvent) => any; + /** + * Setter for the level 1 'menuitem'` event listener. + * @ignore + * @type {function(MenuItemEvent)?} + */ + set onmenuitem(onmenuitem: (arg0: menuitemEvent) => any); + /** + * Level 1 'menuitem'` event listener. + * @type {function(menuitemEvent)?} + */ + get onmenuitem(): (arg0: menuitemEvent) => any; + /** + * The `TrayMenu` instance for the application. + * @type {TrayMenu} + */ + get tray(): TrayMenu; + /** + * The `SystemMenu` instance for the application. + * @type {SystemMenu} + */ + get system(): SystemMenu; + /** + * The `ContextMenu` instance for the application. + * @type {ContextMenu} + */ + get context(): ContextMenu; + #private; + } + /** + * A `Menu` instance that represents a context menu. + */ + export class ContextMenu extends Menu { + constructor(); + } + /** + * A `Menu` instance that represents the system menu. + */ + export class SystemMenu extends Menu { + constructor(); + } + /** + * A `Menu` instance that represents the tray menu. + */ + export class TrayMenu extends Menu { + constructor(); + } + /** + * The application tray menu. + * @type {TrayMenu} + */ + export const tray: TrayMenu; + /** + * The application system menu. + * @type {SystemMenu} + */ + export const system: SystemMenu; + /** + * The application context menu. + * @type {ContextMenu} + */ + export const context: ContextMenu; + /** + * The application menus container. + * @type {MenuContainer} + */ + export const container: MenuContainer; + export default container; + import ipc from "ipc"; +} +declare module "internal/events" { + /** + * An event dispatched when an application URL is opening the application. + */ + export class ApplicationURLEvent extends Event { + /** + * `ApplicationURLEvent` class constructor. + * @param {string=} [type] + * @param {object=} [options] + */ + constructor(type?: string | undefined, options?: object | undefined); + /** + * `true` if the application URL is valid (parses correctly). + * @type {boolean} + */ + get isValid(): boolean; + /** + * Data associated with the `ApplicationURLEvent`. + * @type {?any} + */ + get data(): any; + /** + * The original source URI + * @type {?string} + */ + get source(): string; + /** + * The `URL` for the `ApplicationURLEvent`. + * @type {?URL} + */ + get url(): URL; + /** + * String tag name for an `ApplicationURLEvent` instance. + * @type {string} + */ + get [Symbol.toStringTag](): string; + #private; + } + /** + * An event dispacted for a registered global hotkey expression. + */ + export class HotKeyEvent extends MessageEvent { + /** + * `HotKeyEvent` class constructor. + * @ignore + * @param {string=} [type] + * @param {object=} [data] + */ + constructor(type?: string | undefined, data?: object | undefined); + /** + * The global unique ID for this hotkey binding. + * @type {number?} + */ + get id(): number; + /** + * The computed hash for this hotkey binding. + * @type {number?} + */ + get hash(): number; + /** + * The normalized hotkey expression as a sequence of tokens. + * @type {string[]} + */ + get sequence(): string[]; + /** + * The original expression of the hotkey binding. + * @type {string?} + */ + get expression(): string; + } + /** + * An event dispacted when a menu item is selected. + */ + export class MenuItemEvent extends MessageEvent { + /** + * `MenuItemEvent` class constructor + * @ignore + * @param {string=} [type] + * @param {object=} [data] + * @param {import('../application/menu.js').Menu} menu + */ + constructor(type?: string | undefined, data?: object | undefined, menu?: import("application/menu").Menu); + /** + * The `Menu` this event has been dispatched for. + * @type {import('../application/menu.js').Menu?} + */ + get menu(): import("application/menu").Menu; + /** + * The title of the menu item. + * @type {string?} + */ + get title(): string; + /** + * An optional tag value for the menu item that may also be the + * parent menu item title. + * @type {string?} + */ + get tag(): string; + /** + * The parent title of the menu item. + * @type {string?} + */ + get parent(): string; + #private; + } + /** + * An event dispacted when the application receives an OS signal + */ + export class SignalEvent extends MessageEvent { + /** + * `SignalEvent` class constructor + * @ignore + * @param {string=} [type] + * @param {object=} [options] + */ + constructor(type?: string | undefined, options?: object | undefined); + /** + * The code of the signal. + * @type {import('../process/signal.js').signal} + */ + get code(): any; + /** + * The name of the signal. + * @type {string} + */ + get name(): string; + /** + * An optional message describing the signal + * @type {string} + */ + get message(): string; + #private; + } + namespace _default { + export { ApplicationURLEvent }; + export { MenuItemEvent }; + export { SignalEvent }; + export { HotKeyEvent }; + } + export default _default; +} +declare module "path/well-known" { + /** + * Well known path to the user's "Downloads" folder. + * @type {?string} + */ + export const DOWNLOADS: string | null; + /** + * Well known path to the user's "Documents" folder. + * @type {?string} + */ + export const DOCUMENTS: string | null; + /** + * Well known path to the user's "Pictures" folder. + * @type {?string} + */ + export const PICTURES: string | null; + /** + * Well known path to the user's "Desktop" folder. + * @type {?string} + */ + export const DESKTOP: string | null; + /** + * Well known path to the user's "Videos" folder. + * @type {?string} + */ + export const VIDEOS: string | null; + /** + * Well known path to the user's "Music" folder. + * @type {?string} + */ + export const MUSIC: string | null; + /** + * Well known path to the application's "resources" folder. + * @type {?string} + */ + export const RESOURCES: string | null; + /** + * Well known path to the application's "config" folder. + * @type {?string} + */ + export const CONFIG: string | null; + /** + * Well known path to the application's public "media" folder. + * @type {?string} + */ + export const MEDIA: string | null; + /** + * Well known path to the application's "data" folder. + * @type {?string} + */ + export const DATA: string | null; + /** + * Well known path to the application's "log" folder. + * @type {?string} + */ + export const LOG: string | null; + /** + * Well known path to the application's "tmp" folder. + * @type {?string} + */ + export const TMP: string | null; + /** + * Well known path to the application's "home" folder. + * This may be the user's HOME directory or the application container sandbox. + * @type {?string} + */ + export const HOME: string | null; + namespace _default { + export { DOWNLOADS }; + export { DOCUMENTS }; + export { RESOURCES }; + export { PICTURES }; + export { DESKTOP }; + export { VIDEOS }; + export { CONFIG }; + export { MEDIA }; + export { MUSIC }; + export { HOME }; + export { DATA }; + export { LOG }; + export { TMP }; + } + export default _default; +} +declare module "os" { + /** + * Returns the operating system CPU architecture for which Socket was compiled. + * @returns {string} - 'arm64', 'ia32', 'x64', or 'unknown' + */ + export function arch(): string; + /** + * Returns an array of objects containing information about each CPU/core. + * @returns {Array} cpus - An array of objects containing information about each CPU/core. + * The properties of the objects are: + * - model `` - CPU model name. + * - speed `` - CPU clock speed (in MHz). + * - times `` - An object containing the fields user, nice, sys, idle, irq representing the number of milliseconds the CPU has spent in each mode. + * - user `` - Time spent by this CPU or core in user mode. + * - nice `` - Time spent by this CPU or core in user mode with low priority (nice). + * - sys `` - Time spent by this CPU or core in system mode. + * - idle `` - Time spent by this CPU or core in idle mode. + * - irq `` - Time spent by this CPU or core in IRQ mode. + * @see {@link https://nodejs.org/api/os.html#os_os_cpus} + */ + export function cpus(): Array; + /** + * Returns an object containing network interfaces that have been assigned a network address. + * @returns {object} - An object containing network interfaces that have been assigned a network address. + * Each key on the returned object identifies a network interface. The associated value is an array of objects that each describe an assigned network address. + * The properties available on the assigned network address object include: + * - address `` - The assigned IPv4 or IPv6 address. + * - netmask `` - The IPv4 or IPv6 network mask. + * - family `` - The address family ('IPv4' or 'IPv6'). + * - mac `` - The MAC address of the network interface. + * - internal `` - Indicates whether the network interface is a loopback interface. + * - scopeid `` - The numeric scope ID (only specified when family is 'IPv6'). + * - cidr `` - The CIDR notation of the interface. + * @see {@link https://nodejs.org/api/os.html#os_os_networkinterfaces} + */ + export function networkInterfaces(): object; + /** + * Returns the operating system platform. + * @returns {string} - 'android', 'cygwin', 'freebsd', 'linux', 'darwin', 'ios', 'openbsd', 'win32', or 'unknown' + * @see {@link https://nodejs.org/api/os.html#os_os_platform} + * The returned value is equivalent to `process.platform`. + */ + export function platform(): string; + /** + * Returns the operating system name. + * @returns {string} - 'CYGWIN_NT', 'Mac', 'Darwin', 'FreeBSD', 'Linux', 'OpenBSD', 'Windows_NT', 'Win32', or 'Unknown' + * @see {@link https://nodejs.org/api/os.html#os_os_type} + */ + export function type(): string; + /** + * @returns {boolean} - `true` if the operating system is Windows. + */ + export function isWindows(): boolean; + /** + * @returns {string} - The operating system's default directory for temporary files. + */ + export function tmpdir(): string; + /** + * Get resource usage. + */ + export function rusage(): any; + /** + * Returns the system uptime in seconds. + * @returns {number} - The system uptime in seconds. + */ + export function uptime(): number; + /** + * Returns the operating system name. + * @returns {string} - The operating system name. + */ + export function uname(): string; + /** + * It's implemented in process.hrtime.bigint() + * @ignore + */ + export function hrtime(): any; + /** + * Node.js doesn't have this method. + * @ignore + */ + export function availableMemory(): any; + /** + * The host operating system. This value can be one of: + * - android + * - android-emulator + * - iphoneos + * - iphone-simulator + * - linux + * - macosx + * - unix + * - unknown + * - win32 + * @ignore + * @return {'android'|'android-emulator'|'iphoneos'|iphone-simulator'|'linux'|'macosx'|unix'|unknown'|win32'} + */ + export function host(): "android" | "android-emulator" | "iphoneos" | iphone; + /** + * Returns the home directory of the current user. + * @return {string} + */ + export function homedir(): string; + export { constants }; + /** + * @type {string} + * The operating system's end-of-line marker. `'\r\n'` on Windows and `'\n'` on POSIX. + */ + export const EOL: string; + export default exports; + import constants from "os/constants"; + import * as exports from "os"; + namespace ___home_werle_repos_socketsupply_socket_api_os_ { } +} +declare module "process/signal" { + /** + * Converts an `signal` code to its corresponding string message. + * @param {import('./os/constants.js').signal} {code} + * @return {string} + */ + export function toString(code: any): string; + /** + * Gets the code for a given 'signal' name. + * @param {string|number} name + * @return {signal} + */ + export function getCode(name: string | number): signal; + /** + * Gets the name for a given 'signal' code + * @return {string} + * @param {string|number} code + */ + export function getName(code: string | number): string; + /** + * Gets the message for a 'signal' code. + * @param {number|string} code + * @return {string} + */ + export function getMessage(code: number | string): string; + /** + * Add a signal event listener. + * @param {string|number} signal + * @param {function(SignalEvent)} callback + * @param {{ once?: boolean }=} [options] + */ + export function addEventListener(signalName: any, callback: (arg0: SignalEvent) => any, options?: { + once?: boolean; + } | undefined): void; + /** + * Remove a signal event listener. + * @param {string|number} signal + * @param {function(SignalEvent)} callback + * @param {{ once?: boolean }=} [options] + */ + export function removeEventListener(signalName: any, callback: (arg0: SignalEvent) => any, options?: { + once?: boolean; + } | undefined): void; + export { constants }; + export const channel: ipc.IPCBroadcastChannel; + export const SIGHUP: any; + export const SIGINT: any; + export const SIGQUIT: any; + export const SIGILL: any; + export const SIGTRAP: any; + export const SIGABRT: any; + export const SIGIOT: any; + export const SIGBUS: any; + export const SIGFPE: any; + export const SIGKILL: any; + export const SIGUSR1: any; + export const SIGSEGV: any; + export const SIGUSR2: any; + export const SIGPIPE: any; + export const SIGALRM: any; + export const SIGTERM: any; + export const SIGCHLD: any; + export const SIGCONT: any; + export const SIGSTOP: any; + export const SIGTSTP: any; + export const SIGTTIN: any; + export const SIGTTOU: any; + export const SIGURG: any; + export const SIGXCPU: any; + export const SIGXFSZ: any; + export const SIGVTALRM: any; + export const SIGPROF: any; + export const SIGWINCH: any; + export const SIGIO: any; + export const SIGINFO: any; + export const SIGSYS: any; + export const strings: { + [x: number]: string; + }; + namespace _default { + export { addEventListener }; + export { removeEventListener }; + export { constants }; + export { channel }; + export { strings }; + export { toString }; + export { getName }; + export { getCode }; + export { getMessage }; + export { SIGHUP }; + export { SIGINT }; + export { SIGQUIT }; + export { SIGILL }; + export { SIGTRAP }; + export { SIGABRT }; + export { SIGIOT }; + export { SIGBUS }; + export { SIGFPE }; + export { SIGKILL }; + export { SIGUSR1 }; + export { SIGSEGV }; + export { SIGUSR2 }; + export { SIGPIPE }; + export { SIGALRM }; + export { SIGTERM }; + export { SIGCHLD }; + export { SIGCONT }; + export { SIGSTOP }; + export { SIGTSTP }; + export { SIGTTIN }; + export { SIGTTOU }; + export { SIGURG }; + export { SIGXCPU }; + export { SIGXFSZ }; + export { SIGVTALRM }; + export { SIGPROF }; + export { SIGWINCH }; + export { SIGIO }; + export { SIGINFO }; + export { SIGSYS }; + } + export default _default; + export type signal = any; + import { SignalEvent } from "internal/events"; + import { signal as constants } from "os/constants"; + import ipc from "ipc"; +} +declare module "internal/streams/web" { + export class ByteLengthQueuingStrategy { + constructor(e: any); + _byteLengthQueuingStrategyHighWaterMark: any; + get highWaterMark(): any; + get size(): (e: any) => any; + } + export class CountQueuingStrategy { + constructor(e: any); + _countQueuingStrategyHighWaterMark: any; + get highWaterMark(): any; + get size(): () => number; + } + export class ReadableByteStreamController { + get byobRequest(): any; + get desiredSize(): number; + close(): void; + enqueue(e: any): void; + error(e?: any): void; + _pendingPullIntos: v; + [T](e: any): any; + [C](e: any): any; + [P](): void; + } + export class ReadableStream { + static from(e: any): any; + constructor(e?: {}, t?: {}); + get locked(): boolean; + cancel(e?: any): any; + getReader(e?: any): ReadableStreamBYOBReader | ReadableStreamDefaultReader; + pipeThrough(e: any, t?: {}): any; + pipeTo(e: any, t?: {}): any; + tee(): any; + values(e?: any): any; + } + export class ReadableStreamBYOBReader { + constructor(e: any); + _readIntoRequests: v; + get closed(): any; + cancel(e?: any): any; + read(e: any, t?: {}): any; + releaseLock(): void; + } + export class ReadableStreamBYOBRequest { + get view(): any; + respond(e: any): void; + respondWithNewView(e: any): void; + } + export class ReadableStreamDefaultController { + get desiredSize(): number; + close(): void; + enqueue(e?: any): void; + error(e?: any): void; + [T](e: any): any; + [C](e: any): void; + [P](): void; + } + export class ReadableStreamDefaultReader { + constructor(e: any); + _readRequests: v; + get closed(): any; + cancel(e?: any): any; + read(): any; + releaseLock(): void; + } + export class TransformStream { + constructor(e?: {}, t?: {}, r?: {}); + get readable(): any; + get writable(): any; + } + export class TransformStreamDefaultController { + get desiredSize(): number; + enqueue(e?: any): void; + error(e?: any): void; + terminate(): void; + } + export class WritableStream { + constructor(e?: {}, t?: {}); + get locked(): boolean; + abort(e?: any): any; + close(): any; + getWriter(): WritableStreamDefaultWriter; + } + export class WritableStreamDefaultController { + get abortReason(): any; + get signal(): any; + error(e?: any): void; + [w](e: any): any; + [R](): void; + } + export class WritableStreamDefaultWriter { + constructor(e: any); + _ownerWritableStream: any; + get closed(): any; + get desiredSize(): number; + get ready(): any; + abort(e?: any): any; + close(): any; + releaseLock(): void; + write(e?: any): any; + } + class v { + _cursor: number; + _size: number; + _front: { + _elements: any[]; + _next: any; + }; + _back: { + _elements: any[]; + _next: any; + }; + get length(): number; + push(e: any): void; + shift(): any; + forEach(e: any): void; + peek(): any; + } + const T: unique symbol; + const C: unique symbol; + const P: unique symbol; + const w: unique symbol; + const R: unique symbol; + export {}; +} +declare module "internal/streams" { + const _default: any; + export default _default; + import { ReadableStream } from "internal/streams/web"; + import { ReadableStreamBYOBReader } from "internal/streams/web"; + import { ReadableByteStreamController } from "internal/streams/web"; + import { ReadableStreamBYOBRequest } from "internal/streams/web"; + import { ReadableStreamDefaultController } from "internal/streams/web"; + import { ReadableStreamDefaultReader } from "internal/streams/web"; + import { WritableStream } from "internal/streams/web"; + import { WritableStreamDefaultController } from "internal/streams/web"; + import { WritableStreamDefaultWriter } from "internal/streams/web"; + import { TransformStream } from "internal/streams/web"; + import { TransformStreamDefaultController } from "internal/streams/web"; + import { ByteLengthQueuingStrategy } from "internal/streams/web"; + import { CountQueuingStrategy } from "internal/streams/web"; + export { ReadableStream, ReadableStreamBYOBReader, ReadableByteStreamController, ReadableStreamBYOBRequest, ReadableStreamDefaultController, ReadableStreamDefaultReader, WritableStream, WritableStreamDefaultController, WritableStreamDefaultWriter, TransformStream, TransformStreamDefaultController, ByteLengthQueuingStrategy, CountQueuingStrategy }; +} +declare module "stream/web" { + export const TextEncoderStream: typeof UnsupportedStreamInterface; + export const TextDecoderStream: { + new (label?: string, options?: TextDecoderOptions): TextDecoderStream; + prototype: TextDecoderStream; + } | typeof UnsupportedStreamInterface; + export const CompressionStream: { + new (format: CompressionFormat): CompressionStream; + prototype: CompressionStream; + } | typeof UnsupportedStreamInterface; + export const DecompressionStream: { + new (format: CompressionFormat): DecompressionStream; + prototype: DecompressionStream; + } | typeof UnsupportedStreamInterface; + export default exports; + import { ReadableStream } from "internal/streams"; + import { ReadableStreamBYOBReader } from "internal/streams"; + import { ReadableByteStreamController } from "internal/streams"; + import { ReadableStreamBYOBRequest } from "internal/streams"; + import { ReadableStreamDefaultController } from "internal/streams"; + import { ReadableStreamDefaultReader } from "internal/streams"; + import { WritableStream } from "internal/streams"; + import { WritableStreamDefaultController } from "internal/streams"; + import { WritableStreamDefaultWriter } from "internal/streams"; + import { TransformStream } from "internal/streams"; + import { TransformStreamDefaultController } from "internal/streams"; + import { ByteLengthQueuingStrategy } from "internal/streams"; + import { CountQueuingStrategy } from "internal/streams"; + class UnsupportedStreamInterface { + } + import * as exports from "stream/web"; + namespace ___home_werle_repos_socketsupply_socket_api_stream_web_ { } + export { ReadableStream, ReadableStreamBYOBReader, ReadableByteStreamController, ReadableStreamBYOBRequest, ReadableStreamDefaultController, ReadableStreamDefaultReader, WritableStream, WritableStreamDefaultController, WritableStreamDefaultWriter, TransformStream, TransformStreamDefaultController, ByteLengthQueuingStrategy, CountQueuingStrategy }; +} +declare module "stream" { + export function pipelinePromise(...streams: any[]): Promise; + export function pipeline(stream: any, ...streams: any[]): any; + export function isStream(stream: any): boolean; + export function isStreamx(stream: any): boolean; + export function getStreamError(stream: any): any; + export function isReadStreamx(stream: any): any; + export { web }; + export class FixedFIFO { + constructor(hwm: any); + buffer: any[]; + mask: number; + top: number; + btm: number; + next: any; + clear(): void; + push(data: any): boolean; + shift(): any; + peek(): any; + isEmpty(): boolean; + } + export class FIFO { + constructor(hwm: any); + hwm: any; + head: FixedFIFO; + tail: FixedFIFO; + length: number; + clear(): void; + push(val: any): void; + shift(): any; + peek(): any; + isEmpty(): boolean; + } + export class WritableState { + constructor(stream: any, { highWaterMark, map, mapWritable, byteLength, byteLengthWritable }?: { + highWaterMark?: number; + map?: any; + mapWritable: any; + byteLength: any; + byteLengthWritable: any; + }); + stream: any; + queue: FIFO; + highWaterMark: number; + buffered: number; + error: any; + pipeline: any; + drains: any; + byteLength: any; + map: any; + afterWrite: any; + afterUpdateNextTick: any; + get ended(): boolean; + push(data: any): boolean; + shift(): any; + end(data: any): void; + autoBatch(data: any, cb: any): any; + update(): void; + updateNonPrimary(): void; + continueUpdate(): boolean; + updateCallback(): void; + updateNextTick(): void; + } + export class ReadableState { + constructor(stream: any, { highWaterMark, map, mapReadable, byteLength, byteLengthReadable }?: { + highWaterMark?: number; + map?: any; + mapReadable: any; + byteLength: any; + byteLengthReadable: any; + }); + stream: any; + queue: FIFO; + highWaterMark: number; + buffered: number; + readAhead: boolean; + error: any; + pipeline: Pipeline; + byteLength: any; + map: any; + pipeTo: any; + afterRead: any; + afterUpdateNextTick: any; + get ended(): boolean; + pipe(pipeTo: any, cb: any): void; + push(data: any): boolean; + shift(): any; + unshift(data: any): void; + read(): any; + drain(): void; + update(): void; + updateNonPrimary(): void; + continueUpdate(): boolean; + updateCallback(): void; + updateNextTick(): void; + } + export class TransformState { + constructor(stream: any); + data: any; + afterTransform: any; + afterFinal: any; + } + export class Pipeline { + constructor(src: any, dst: any, cb: any); + from: any; + to: any; + afterPipe: any; + error: any; + pipeToFinished: boolean; + finished(): void; + done(stream: any, err: any): void; + } + export class Stream extends EventEmitter { + constructor(opts: any); + _duplexState: number; + _readableState: any; + _writableState: any; + _open(cb: any): void; + _destroy(cb: any): void; + _predestroy(): void; + get readable(): boolean; + get writable(): boolean; + get destroyed(): boolean; + get destroying(): boolean; + destroy(err: any): void; + } + export class Readable extends Stream { + static _fromAsyncIterator(ite: any, opts: any): Readable; + static from(data: any, opts: any): any; + static isBackpressured(rs: any): boolean; + static isPaused(rs: any): boolean; + _readableState: ReadableState; + _read(cb: any): void; + pipe(dest: any, cb: any): any; + read(): any; + push(data: any): boolean; + unshift(data: any): void; + resume(): this; + pause(): this; + } + export class Writable extends Stream { + static isBackpressured(ws: any): boolean; + static drained(ws: any): Promise; + _writableState: WritableState; + _writev(batch: any, cb: any): void; + _write(data: any, cb: any): void; + _final(cb: any): void; + write(data: any): boolean; + end(data: any): this; + } + export class Duplex extends Readable { + _writableState: WritableState; + _writev(batch: any, cb: any): void; + _write(data: any, cb: any): void; + _final(cb: any): void; + write(data: any): boolean; + end(data: any): this; + } + export class Transform extends Duplex { + _transformState: TransformState; + _transform(data: any, cb: any): void; + _flush(cb: any): void; + } + export class PassThrough extends Transform { + } + const _default: typeof Stream & { + web: typeof web; + Readable: typeof Readable; + Writable: typeof Writable; + Duplex: typeof Duplex; + Transform: typeof Transform; + PassThrough: typeof PassThrough; + pipeline: typeof pipeline & { + [x: symbol]: typeof pipelinePromise; + }; + }; + export default _default; + import web from "stream/web"; + import { EventEmitter } from "events"; +} +declare module "tty" { + export function WriteStream(fd: any): Writable; + export function ReadStream(fd: any): Readable; + export function isatty(fd: any): boolean; + namespace _default { + export { WriteStream }; + export { ReadStream }; + export { isatty }; + } + export default _default; + import { Writable } from "stream"; + import { Readable } from "stream"; +} +declare module "process" { + /** + * Adds callback to the 'nextTick' queue. + * @param {Function} callback + */ + export function nextTick(callback: Function, ...args: any[]): void; + /** + * Computed high resolution time as a `BigInt`. + * @param {Array?} [time] + * @return {bigint} + */ + export function hrtime(time?: Array | null): bigint; + export namespace hrtime { + function bigint(): any; + } + /** + * @param {number=} [code=0] - The exit code. Default: 0. + */ + export function exit(code?: number | undefined): Promise; + /** + * Returns an object describing the memory usage of the Node.js process measured in bytes. + * @returns {Object} + */ + export function memoryUsage(): any; + export namespace memoryUsage { + function rss(): any; + } + export class ProcessEnvironmentEvent extends Event { + constructor(type: any, key: any, value: any); + key: any; + value: any; + } + export class ProcessEnvironment extends EventTarget { + get [Symbol.toStringTag](): string; + } + export const env: ProcessEnvironment; + export default process; + const process: any; +} +declare module "path/path" { + /** + * The path.resolve() method resolves a sequence of paths or path segments into an absolute path. + * @param {strig} ...paths + * @returns {string} + * @see {@link https://nodejs.org/api/path.html#path_path_resolve_paths} + */ + export function resolve(options: any, ...components: any[]): string; + /** + * Computes current working directory for a path + * @param {object=} [opts] + * @param {boolean=} [opts.posix] Set to `true` to force POSIX style path + * @return {string} + */ + export function cwd(opts?: object | undefined): string; + /** + * Computed location origin. Defaults to `socket:///` if not available. + * @return {string} + */ + export function origin(): string; + /** + * Computes the relative path from `from` to `to`. + * @param {object} options + * @param {PathComponent} from + * @param {PathComponent} to + * @return {string} + */ + export function relative(options: object, from: PathComponent, to: PathComponent): string; + /** + * Joins path components. This function may not return an absolute path. + * @param {object} options + * @param {...PathComponent} components + * @return {string} + */ + export function join(options: object, ...components: PathComponent[]): string; + /** + * Computes directory name of path. + * @param {object} options + * @param {...PathComponent} components + * @return {string} + */ + export function dirname(options: object, path: any): string; + /** + * Computes base name of path. + * @param {object} options + * @param {...PathComponent} components + * @return {string} + */ + export function basename(options: object, path: any): string; + /** + * Computes extension name of path. + * @param {object} options + * @param {PathComponent} path + * @return {string} + */ + export function extname(options: object, path: PathComponent): string; + /** + * Computes normalized path + * @param {object} options + * @param {PathComponent} path + * @return {string} + */ + export function normalize(options: object, path: PathComponent): string; + /** + * Formats `Path` object into a string. + * @param {object} options + * @param {object|Path} path + * @return {string} + */ + export function format(options: object, path: object | Path): string; + /** + * Parses input `path` into a `Path` instance. + * @param {PathComponent} path + * @return {object} + */ + export function parse(path: PathComponent): object; + /** + * @typedef {(string|Path|URL|{ pathname: string }|{ url: string)} PathComponent + */ + /** + * A container for a parsed Path. + */ + export class Path { + /** + * Creates a `Path` instance from `input` and optional `cwd`. + * @param {PathComponent} input + * @param {string} [cwd] + */ + static from(input: PathComponent, cwd?: string): any; + /** + * `Path` class constructor. + * @protected + * @param {string} pathname + * @param {string} [cwd = Path.cwd()] + */ + protected constructor(); + pattern: { + "__#11@#i": any; + "__#11@#n": {}; + "__#11@#t": {}; + "__#11@#e": {}; + "__#11@#s": {}; + "__#11@#l": boolean; + test(t: {}, r: any): boolean; + exec(t: {}, r: any): { + inputs: any[] | {}[]; + }; + readonly protocol: any; + readonly username: any; + readonly password: any; + readonly hostname: any; + readonly port: any; + readonly pathname: any; + readonly search: any; + readonly hash: any; + readonly hasRegExpGroups: boolean; + }; + url: any; + get pathname(): any; + get protocol(): any; + get href(): any; + /** + * `true` if the path is relative, otherwise `false. + * @type {boolean} + */ + get isRelative(): boolean; + /** + * The working value of this path. + */ + get value(): any; + /** + * The original source, unresolved. + * @type {string} + */ + get source(): string; + /** + * Computed parent path. + * @type {string} + */ + get parent(): string; + /** + * Computed root in path. + * @type {string} + */ + get root(): string; + /** + * Computed directory name in path. + * @type {string} + */ + get dir(): string; + /** + * Computed base name in path. + * @type {string} + */ + get base(): string; + /** + * Computed base name in path without path extension. + * @type {string} + */ + get name(): string; + /** + * Computed extension name in path. + * @type {string} + */ + get ext(): string; + /** + * The computed drive, if given in the path. + * @type {string?} + */ + get drive(): string; + /** + * @return {URL} + */ + toURL(): URL; + /** + * Converts this `Path` instance to a string. + * @return {string} + */ + toString(): string; + /** + * @ignore + */ + inspect(): { + root: string; + dir: string; + base: string; + ext: string; + name: string; + }; + /** + * @ignore + */ + [Symbol.toStringTag](): string; + #private; + } + export default Path; + export type PathComponent = (string | Path | URL | { + pathname: string; + } | { + url: string; + }); + import { URL } from "url/index"; +} +declare module "path/mounts" { + const _default: {}; + export default _default; +} +declare module "path/win32" { + /** + * Computes current working directory for a path + * @param {string} + */ + export function cwd(): any; + /** + * Resolves path components to an absolute path. + * @param {...PathComponent} components + * @return {string} + */ + export function resolve(...components: PathComponent[]): string; + /** + * Joins path components. This function may not return an absolute path. + * @param {...PathComponent} components + * @return {string} + */ + export function join(...components: PathComponent[]): string; + /** + * Computes directory name of path. + * @param {PathComponent} path + * @return {string} + */ + export function dirname(path: PathComponent): string; + /** + * Computes base name of path. + * @param {PathComponent} path + * @param {string=} [suffix] + * @return {string} + */ + export function basename(path: PathComponent, suffix?: string | undefined): string; + /** + * Computes extension name of path. + * @param {PathComponent} path + * @return {string} + */ + export function extname(path: PathComponent): string; + /** + * Predicate helper to determine if path is absolute. + * @param {PathComponent} path + * @return {boolean} + */ + export function isAbsolute(path: PathComponent): boolean; + /** + * Parses input `path` into a `Path` instance. + * @param {PathComponent} path + * @return {Path} + */ + export function parse(path: PathComponent): Path; + /** + * Formats `Path` object into a string. + * @param {object|Path} path + * @return {string} + */ + export function format(path: object | Path): string; + /** + * Normalizes `path` resolving `..` and `.\` preserving trailing + * slashes. + * @param {string} path + */ + export function normalize(path: string): any; + /** + * Computes the relative path from `from` to `to`. + * @param {string} from + * @param {string} to + * @return {string} + */ + export function relative(from: string, to: string): string; + export default exports; + export namespace win32 { + let sep: "\\"; + let delimiter: ";"; + } + export type PathComponent = import("path/path").PathComponent; + import { Path } from "path/path"; + import * as mounts from "path/mounts"; + import * as posix from "path/posix"; + import { DOWNLOADS } from "path/well-known"; + import { DOCUMENTS } from "path/well-known"; + import { RESOURCES } from "path/well-known"; + import { PICTURES } from "path/well-known"; + import { DESKTOP } from "path/well-known"; + import { VIDEOS } from "path/well-known"; + import { CONFIG } from "path/well-known"; + import { MEDIA } from "path/well-known"; + import { MUSIC } from "path/well-known"; + import { HOME } from "path/well-known"; + import { DATA } from "path/well-known"; + import { LOG } from "path/well-known"; + import { TMP } from "path/well-known"; + import * as exports from "path/win32"; + namespace ___home_werle_repos_socketsupply_socket_api_path_win32_ { } + export { mounts, posix, Path, DOWNLOADS, DOCUMENTS, RESOURCES, PICTURES, DESKTOP, VIDEOS, CONFIG, MEDIA, MUSIC, HOME, DATA, LOG, TMP }; +} +declare module "path/posix" { + /** + * Computes current working directory for a path + * @param {string} + * @return {string} + */ + export function cwd(): string; + /** + * Resolves path components to an absolute path. + * @param {...PathComponent} components + * @return {string} + */ + export function resolve(...components: PathComponent[]): string; + /** + * Joins path components. This function may not return an absolute path. + * @param {...PathComponent} components + * @return {string} + */ + export function join(...components: PathComponent[]): string; + /** + * Computes directory name of path. + * @param {PathComponent} path + * @return {string} + */ + export function dirname(path: PathComponent): string; + /** + * Computes base name of path. + * @param {PathComponent} path + * @param {string=} [suffix] + * @return {string} + */ + export function basename(path: PathComponent, suffix?: string | undefined): string; + /** + * Computes extension name of path. + * @param {PathComponent} path + * @return {string} + */ + export function extname(path: PathComponent): string; + /** + * Predicate helper to determine if path is absolute. + * @param {PathComponent} path + * @return {boolean} + */ + export function isAbsolute(path: PathComponent): boolean; + /** + * Parses input `path` into a `Path` instance. + * @param {PathComponent} path + * @return {Path} + */ + export function parse(path: PathComponent): Path; + /** + * Formats `Path` object into a string. + * @param {object|Path} path + * @return {string} + */ + export function format(path: object | Path): string; + /** + * Normalizes `path` resolving `..` and `./` preserving trailing + * slashes. + * @param {string} path + */ + export function normalize(path: string): any; + /** + * Computes the relative path from `from` to `to`. + * @param {string} from + * @param {string} to + * @return {string} + */ + export function relative(from: string, to: string): string; + export default exports; + export namespace posix { + let sep: "/"; + let delimiter: ":"; + } + export type PathComponent = import("path/path").PathComponent; + import { Path } from "path/path"; + import * as mounts from "path/mounts"; + import * as win32 from "path/win32"; + import { DOWNLOADS } from "path/well-known"; + import { DOCUMENTS } from "path/well-known"; + import { RESOURCES } from "path/well-known"; + import { PICTURES } from "path/well-known"; + import { DESKTOP } from "path/well-known"; + import { VIDEOS } from "path/well-known"; + import { CONFIG } from "path/well-known"; + import { MEDIA } from "path/well-known"; + import { MUSIC } from "path/well-known"; + import { HOME } from "path/well-known"; + import { DATA } from "path/well-known"; + import { LOG } from "path/well-known"; + import { TMP } from "path/well-known"; + import * as exports from "path/posix"; + namespace ___home_werle_repos_socketsupply_socket_api_path_posix_ { } + export { mounts, win32, Path, DOWNLOADS, DOCUMENTS, RESOURCES, PICTURES, DESKTOP, VIDEOS, CONFIG, MEDIA, MUSIC, HOME, DATA, LOG, TMP }; +} +declare module "path/index" { + export default exports; + import * as mounts from "path/mounts"; + import * as posix from "path/posix"; + import * as win32 from "path/win32"; + import { Path } from "path/path"; + import { DOWNLOADS } from "path/well-known"; + import { DOCUMENTS } from "path/well-known"; + import { RESOURCES } from "path/well-known"; + import { PICTURES } from "path/well-known"; + import { DESKTOP } from "path/well-known"; + import { VIDEOS } from "path/well-known"; + import { CONFIG } from "path/well-known"; + import { MEDIA } from "path/well-known"; + import { MUSIC } from "path/well-known"; + import { HOME } from "path/well-known"; + import { DATA } from "path/well-known"; + import { LOG } from "path/well-known"; + import { TMP } from "path/well-known"; + import * as exports from "path/index"; + namespace ___home_werle_repos_socketsupply_socket_api_path_index_ { } + export { mounts, posix, win32, Path, DOWNLOADS, DOCUMENTS, RESOURCES, PICTURES, DESKTOP, VIDEOS, CONFIG, MEDIA, MUSIC, HOME, DATA, LOG, TMP }; +} +declare module "path" { + export const sep: "\\" | "/"; + export const delimiter: ":" | ";"; + export const resolve: typeof posix.win32.resolve; + export const join: typeof posix.win32.join; + export const dirname: typeof posix.win32.dirname; + export const basename: typeof posix.win32.basename; + export const extname: typeof posix.win32.extname; + export const cwd: typeof posix.win32.cwd; + export const isAbsolute: typeof posix.win32.isAbsolute; + export const parse: typeof posix.win32.parse; + export const format: typeof posix.win32.format; + export const normalize: typeof posix.win32.normalize; + export const relative: typeof posix.win32.relative; + const _default: typeof posix | typeof posix.win32; + export default _default; + import { posix } from "path/index"; + import { Path } from "path/index"; + import { win32 } from "path/index"; + import { mounts } from "path/index"; + import { DOWNLOADS } from "path/index"; + import { DOCUMENTS } from "path/index"; + import { RESOURCES } from "path/index"; + import { PICTURES } from "path/index"; + import { DESKTOP } from "path/index"; + import { VIDEOS } from "path/index"; + import { CONFIG } from "path/index"; + import { MEDIA } from "path/index"; + import { MUSIC } from "path/index"; + import { HOME } from "path/index"; + import { DATA } from "path/index"; + import { LOG } from "path/index"; + import { TMP } from "path/index"; + export { Path, posix, win32, mounts, DOWNLOADS, DOCUMENTS, RESOURCES, PICTURES, DESKTOP, VIDEOS, CONFIG, MEDIA, MUSIC, HOME, DATA, LOG, TMP }; +} +declare module "fs/constants" { + /** + * This flag can be used with uv_fs_copyfile() to return an error if the + * destination already exists. + * @type {number} + */ + export const COPYFILE_EXCL: number; + /** + * This flag can be used with uv_fs_copyfile() to attempt to create a reflink. + * If copy-on-write is not supported, a fallback copy mechanism is used. + * @type {number} + */ + export const COPYFILE_FICLONE: number; + /** + * This flag can be used with uv_fs_copyfile() to attempt to create a reflink. + * If copy-on-write is not supported, an error is returned. + * @type {number} + */ + export const COPYFILE_FICLONE_FORCE: number; + /** + * A constant representing a directory entry whose type is unknown. + * It indicates that the type of the file or directory cannot be determined. + * @type {number} + */ + export const UV_DIRENT_UNKNOWN: number; + /** + * A constant representing a directory entry of type file. + * It indicates that the entry is a regular file. + * @type {number} + */ + export const UV_DIRENT_FILE: number; + /** + * A constant epresenting a directory entry of type directory. + * It indicates that the entry is a directory. + * @type {number} + */ + export const UV_DIRENT_DIR: number; + /** + * A constant representing a directory entry of type symbolic link. + * @type {number} + */ + export const UV_DIRENT_LINK: number; + /** + * A constant representing a directory entry of type FIFO (named pipe). + * @type {number} + */ + export const UV_DIRENT_FIFO: number; + /** + * A constant representing a directory entry of type socket. + * @type {number} + */ + export const UV_DIRENT_SOCKET: number; + /** + * A constant representing a directory entry of type character device + * @type {number} + */ + export const UV_DIRENT_CHAR: number; + /** + * A constant representing a directory entry of type block device. + * @type {number} + */ + export const UV_DIRENT_BLOCK: number; + /** + * A constant representing a symlink should target a directory. + * @type {number} + */ + export const UV_FS_SYMLINK_DIR: number; + /** + * A constant representing a symlink should be created as a Windows junction. + * @type {number} + */ + export const UV_FS_SYMLINK_JUNCTION: number; + /** + * A constant representing an opened file for memory mapping on Windows systems. + * @type {number} + */ + export const UV_FS_O_FILEMAP: number; + /** + * Opens a file for read-only access. + * @type {number} + */ + export const O_RDONLY: number; + /** + * Opens a file for write-only access. + * @type {number} + */ + export const O_WRONLY: number; + /** + * Opens a file for both reading and writing. + * @type {number} + */ + export const O_RDWR: number; + /** + * Appends data to the file instead of overwriting. + * @type {number} + */ + export const O_APPEND: number; + /** + * Enables asynchronous I/O notifications. + * @type {number} + */ + export const O_ASYNC: number; + /** + * Ensures file descriptors are closed on `exec()` calls. + * @type {number} + */ + export const O_CLOEXEC: number; + /** + * Creates a new file if it does not exist. + * @type {number} + */ + export const O_CREAT: number; + /** + * Minimizes caching effects for file I/O. + * @type {number} + */ + export const O_DIRECT: number; + /** + * Ensures the opened file is a directory. + * @type {number} + */ + export const O_DIRECTORY: number; + /** + * Writes file data synchronously. + * @type {number} + */ + export const O_DSYNC: number; + /** + * Fails the operation if the file already exists. + * @type {number} + */ + export const O_EXCL: number; + /** + * Enables handling of large files. + * @type {number} + */ + export const O_LARGEFILE: number; + /** + * Prevents updating the file's last access time. + * @type {number} + */ + export const O_NOATIME: number; + /** + * Prevents becoming the controlling terminal for the process. + * @type {number} + */ + export const O_NOCTTY: number; + /** + * Does not follow symbolic links. + * @type {number} + */ + export const O_NOFOLLOW: number; + /** + * Opens the file in non-blocking mode. + * @type {number} + */ + export const O_NONBLOCK: number; + /** + * Alias for `O_NONBLOCK` on some systems. + * @type {number} + */ + export const O_NDELAY: number; + /** + * Obtains a file descriptor for a file but does not open it. + * @type {number} + */ + export const O_PATH: number; + /** + * Writes both file data and metadata synchronously. + * @type {number} + */ + export const O_SYNC: number; + /** + * Creates a temporary file that is not linked to a directory. + * @type {number} + */ + export const O_TMPFILE: number; + /** + * Truncates the file to zero length if it exists. + * @type {number} + */ + export const O_TRUNC: number; + /** + * Bitmask for extracting the file type from a mode. + * @type {number} + */ + export const S_IFMT: number; + /** + * Indicates a regular file. + * @type {number} + */ + export const S_IFREG: number; + /** + * Indicates a directory. + * @type {number} + */ + export const S_IFDIR: number; + /** + * Indicates a character device. + * @type {number} + */ + export const S_IFCHR: number; + /** + * Indicates a block device. + * @type {number} + */ + export const S_IFBLK: number; + /** + * Indicates a FIFO (named pipe). + * @type {number} + */ + export const S_IFIFO: number; + /** + * Indicates a symbolic link. + * @type {number} + */ + export const S_IFLNK: number; + /** + * Indicates a socket. + * @type {number} + */ + export const S_IFSOCK: number; + /** + * Grants read, write, and execute permissions for the file owner. + * @type {number} + */ + export const S_IRWXU: number; + /** + * Grants read permission for the file owner. + * @type {number} + */ + export const S_IRUSR: number; + /** + * Grants write permission for the file owner. + * @type {number} + */ + export const S_IWUSR: number; + /** + * Grants execute permission for the file owner. + * @type {number} + */ + export const S_IXUSR: number; + /** + * Grants read, write, and execute permissions for the group. + * @type {number} + */ + export const S_IRWXG: number; + /** + * Grants read permission for the group. + * @type {number} + */ + export const S_IRGRP: number; + /** + * Grants write permission for the group. + * @type {number} + */ + export const S_IWGRP: number; + /** + * Grants execute permission for the group. + * @type {number} + */ + export const S_IXGRP: number; + /** + * Grants read, write, and execute permissions for others. + * @type {number} + */ + export const S_IRWXO: number; + /** + * Grants read permission for others. + * @type {number} + */ + export const S_IROTH: number; + /** + * Grants write permission for others. + * @type {number} + */ + export const S_IWOTH: number; + /** + * Grants execute permission for others. + * @type {number} + */ + export const S_IXOTH: number; + /** + * Checks for the existence of a file. + * @type {number} + */ + export const F_OK: number; + /** + * Checks for read permission on a file. + * @type {number} + */ + export const R_OK: number; + /** + * Checks for write permission on a file. + * @type {number} + */ + export const W_OK: number; + /** + * Checks for execute permission on a file. + * @type {number} + */ + export const X_OK: number; + export default exports; + import * as exports from "fs/constants"; + namespace ___home_werle_repos_socketsupply_socket_api_fs_constants_ { } +} +declare module "fs/stream" { + export const DEFAULT_STREAM_HIGH_WATER_MARK: number; + /** + * @typedef {import('./handle.js').FileHandle} FileHandle + */ + /** + * A `Readable` stream for a `FileHandle`. + */ + export class ReadStream extends Readable { + end: any; + start: any; + handle: any; + buffer: ArrayBuffer; + signal: any; + timeout: any; + bytesRead: number; + shouldEmitClose: boolean; + /** + * Sets file handle for the ReadStream. + * @param {FileHandle} handle + */ + setHandle(handle: FileHandle): void; + /** + * The max buffer size for the ReadStream. + */ + get highWaterMark(): number; + /** + * Relative or absolute path of the underlying `FileHandle`. + */ + get path(): any; + /** + * `true` if the stream is in a pending state. + */ + get pending(): boolean; + _open(callback: any): Promise; + _read(callback: any): Promise; + } + export namespace ReadStream { + export { DEFAULT_STREAM_HIGH_WATER_MARK as highWaterMark }; + } + /** + * A `Writable` stream for a `FileHandle`. + */ + export class WriteStream extends Writable { + start: any; + handle: any; + signal: any; + timeout: any; + bytesWritten: number; + shouldEmitClose: boolean; + /** + * Sets file handle for the WriteStream. + * @param {FileHandle} handle + */ + setHandle(handle: FileHandle): void; + /** + * The max buffer size for the Writetream. + */ + get highWaterMark(): number; + /** + * Relative or absolute path of the underlying `FileHandle`. + */ + get path(): any; + /** + * `true` if the stream is in a pending state. + */ + get pending(): boolean; + _open(callback: any): Promise; + _write(buffer: any, callback: any): any; + } + export namespace WriteStream { + export { DEFAULT_STREAM_HIGH_WATER_MARK as highWaterMark }; + } + export const FileReadStream: typeof exports.ReadStream; + export const FileWriteStream: typeof exports.WriteStream; + export default exports; + export type FileHandle = import("fs/handle").FileHandle; + import { Readable } from "stream"; + import { Writable } from "stream"; + import * as exports from "fs/stream"; + namespace ___home_werle_repos_socketsupply_socket_api_fs_stream_ { } +} +declare module "fs/flags" { + export function normalizeFlags(flags: any): number; + export default exports; + import * as exports from "fs/flags"; + namespace ___home_werle_repos_socketsupply_socket_api_fs_flags_ { } +} +declare module "diagnostics/channels" { + /** + * Normalizes a channel name to lower case replacing white space, + * hyphens (-), underscores (_), with dots (.). + * @ignore + */ + export function normalizeName(group: any, name: any): string; + /** + * Used to preallocate a minimum sized array of subscribers for + * a channel. + * @ignore + */ + export const MIN_CHANNEL_SUBSCRIBER_SIZE: 64; + /** + * A general interface for diagnostic channels that can be subscribed to. + */ + export class Channel { + constructor(name: any); + name: any; + group: any; + /** + * Computed subscribers for all channels in this group. + * @type {Array} + */ + get subscribers(): Function[]; + /** + * Accessor for determining if channel has subscribers. This + * is always `false` for `Channel instances and `true` for `ActiveChannel` + * instances. + */ + get hasSubscribers(): boolean; + /** + * Computed number of subscribers for this channel. + */ + get length(): number; + /** + * Resets channel state. + * @param {(boolean)} [shouldOrphan = false] + */ + reset(shouldOrphan?: (boolean)): void; + channel(name: any): Channel; + /** + * Adds an `onMessage` subscription callback to the channel. + * @return {boolean} + */ + subscribe(_: any, onMessage: any): boolean; + /** + * Removes an `onMessage` subscription callback from the channel. + * @param {function} onMessage + * @return {boolean} + */ + unsubscribe(_: any, onMessage: Function): boolean; + /** + * A no-op for `Channel` instances. This function always returns `false`. + * @param {string|object} name + * @param {object=} [message] + * @return Promise + */ + publish(name: string | object, message?: object | undefined): Promise; + /** + * Returns a string representation of the `ChannelRegistry`. + * @ignore + */ + toString(): any; + /** + * Iterator interface + * @ignore + */ + get [Symbol.iterator](): any[]; + /** + * The `Channel` string tag. + * @ignore + */ + [Symbol.toStringTag](): string; + #private; + } + /** + * An `ActiveChannel` is a prototype implementation for a `Channel` + * that provides an interface what is considered an "active" channel. The + * `hasSubscribers` accessor always returns `true` for this class. + */ + export class ActiveChannel extends Channel { + unsubscribe(onMessage: any): boolean; + /** + * @param {object|any} message + * @return Promise + */ + publish(message: object | any): Promise; + } + /** + * A container for a grouping of channels that are named and owned + * by this group. A `ChannelGroup` can also be a regular channel. + */ + export class ChannelGroup extends Channel { + /** + * @param {Array} channels + * @param {string} name + */ + constructor(name: string, channels: Array); + channels: Channel[]; + /** + * Subscribe to a channel or selection of channels in this group. + * @param {string} name + * @return {boolean} + */ + subscribe(name: string, onMessage: any): boolean; + /** + * Unsubscribe from a channel or selection of channels in this group. + * @param {string} name + * @return {boolean} + */ + unsubscribe(name: string, onMessage: any): boolean; + /** + * Gets or creates a channel for this group. + * @param {string} name + * @return {Channel} + */ + channel(name: string): Channel; + /** + * Select a test of channels from this group. + * The following syntax is supported: + * - One Channel: `group.channel` + * - All Channels: `*` + * - Many Channel: `group.*` + * - Collections: `['group.a', 'group.b', 'group.c'] or `group.a,group.b,group.c` + * @param {string|Array} keys + * @param {(boolean)} [hasSubscribers = false] - Enforce subscribers in selection + * @return {Array<{name: string, channel: Channel}>} + */ + select(keys: string | Array, hasSubscribers?: (boolean)): Array<{ + name: string; + channel: Channel; + }>; + } + /** + * An object mapping of named channels to `WeakRef` instances. + */ + export const registry: { + /** + * Subscribes callback `onMessage` to channel of `name`. + * @param {string} name + * @param {function} onMessage + * @return {boolean} + */ + subscribe(name: string, onMessage: Function): boolean; + /** + * Unsubscribes callback `onMessage` from channel of `name`. + * @param {string} name + * @param {function} onMessage + * @return {boolean} + */ + unsubscribe(name: string, onMessage: Function): boolean; + /** + * Predicate to determine if a named channel has subscribers. + * @param {string} name + */ + hasSubscribers(name: string): boolean; + /** + * Get or set a channel by `name`. + * @param {string} name + * @return {Channel} + */ + channel(name: string): Channel; + /** + * Creates a `ChannelGroup` for a set of channels + * @param {string} name + * @param {Array} [channels] + * @return {ChannelGroup} + */ + group(name: string, channels?: Array): ChannelGroup; + /** + * Get a channel by name. The name is normalized. + * @param {string} name + * @return {Channel?} + */ + get(name: string): Channel | null; + /** + * Checks if a channel is known by name. The name is normalized. + * @param {string} name + * @return {boolean} + */ + has(name: string): boolean; + /** + * Set a channel by name. The name is normalized. + * @param {string} name + * @param {Channel} channel + * @return {Channel?} + */ + set(name: string, channel: Channel): Channel | null; + /** + * Removes a channel by `name` + * @return {boolean} + */ + remove(name: any): boolean; + /** + * Returns a string representation of the `ChannelRegistry`. + * @ignore + */ + toString(): any; + /** + * Returns a JSON representation of the `ChannelRegistry`. + * @return {object} + */ + toJSON(): object; + /** + * The `ChannelRegistry` string tag. + * @ignore + */ + [Symbol.toStringTag](): string; + }; + export default registry; +} +declare module "diagnostics/metric" { + export class Metric { + init(): void; + update(value: any): void; + destroy(): void; + toJSON(): {}; + toString(): string; + [Symbol.iterator](): any; + [Symbol.toStringTag](): string; + } + export default Metric; +} +declare module "diagnostics/window" { + export class RequestAnimationFrameMetric extends Metric { + constructor(options: any); + originalRequestAnimationFrame: typeof requestAnimationFrame; + requestAnimationFrame(callback: any): any; + sampleSize: any; + sampleTick: number; + channel: import("diagnostics/channels").Channel; + value: { + rate: number; + samples: number; + }; + now: number; + samples: Uint8Array; + toJSON(): { + sampleSize: any; + sampleTick: number; + samples: number[]; + rate: number; + now: number; + }; + } + export class FetchMetric extends Metric { + constructor(options: any); + originalFetch: typeof fetch; + channel: import("diagnostics/channels").Channel; + fetch(resource: any, options: any, extra: any): Promise; + } + export class XMLHttpRequestMetric extends Metric { + constructor(options: any); + channel: import("diagnostics/channels").Channel; + patched: { + open: { + (method: string, url: string | URL): void; + (method: string, url: string | URL, async: boolean, username?: string | null, password?: string | null): void; + }; + send: (body?: Document | XMLHttpRequestBodyInit | null) => void; + }; + } + export class WorkerMetric extends Metric { + constructor(options: any); + GlobalWorker: { + new (scriptURL: string | URL, options?: WorkerOptions): Worker; + prototype: Worker; + } | { + new (): {}; + }; + channel: import("diagnostics/channels").Channel; + Worker: { + new (url: any, options: any, ...args: any[]): {}; + }; + } + export const metrics: { + requestAnimationFrame: RequestAnimationFrameMetric; + XMLHttpRequest: XMLHttpRequestMetric; + Worker: WorkerMetric; + fetch: FetchMetric; + channel: import("diagnostics/channels").ChannelGroup; + subscribe(...args: any[]): boolean; + unsubscribe(...args: any[]): boolean; + start(which: any): void; + stop(which: any): void; + }; + namespace _default { + export { metrics }; + } + export default _default; + import { Metric } from "diagnostics/metric"; +} +declare module "diagnostics/runtime" { + /** + * Queries runtime diagnostics. + * @return {Promise} + */ + export function query(type: any): Promise; + /** + * A base container class for diagnostic information. + */ + export class Diagnostic { + /** + * A container for handles related to the diagnostics + */ + static Handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + /** + * Known handles for this diagnostics. + * @type {Diagnostic.Handles} + */ + handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + } + /** + * A container for libuv diagnostics + */ + export class UVDiagnostic extends Diagnostic { + /** + * A container for libuv metrics. + */ + static Metrics: { + new (): { + /** + * The number of event loop iterations. + * @type {number} + */ + loopCount: number; + /** + * Number of events that have been processed by the event handler. + * @type {number} + */ + events: number; + /** + * Number of events that were waiting to be processed when the + * event provider was called. + * @type {number} + */ + eventsWaiting: number; + }; + }; + /** + * Known libuv metrics for this diagnostic. + * @type {UVDiagnostic.Metrics} + */ + metrics: { + new (): { + /** + * The number of event loop iterations. + * @type {number} + */ + loopCount: number; + /** + * Number of events that have been processed by the event handler. + * @type {number} + */ + events: number; + /** + * Number of events that were waiting to be processed when the + * event provider was called. + * @type {number} + */ + eventsWaiting: number; + }; + }; + /** + * The current idle time of the libuv loop + * @type {number} + */ + idleTime: number; + /** + * The number of active requests in the libuv loop + * @type {number} + */ + activeRequests: number; + } + /** + * A container for Core Post diagnostics. + */ + export class PostsDiagnostic extends Diagnostic { + } + /** + * A container for child process diagnostics. + */ + export class ChildProcessDiagnostic extends Diagnostic { + } + /** + * A container for AI diagnostics. + */ + export class AIDiagnostic extends Diagnostic { + /** + * A container for AI LLM diagnostics. + */ + static LLMDiagnostic: { + new (): { + /** + * Known handles for this diagnostics. + * @type {Diagnostic.Handles} + */ + handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * A container for handles related to the diagnostics + */ + Handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * Known AI LLM diagnostics. + * @type {AIDiagnostic.LLMDiagnostic} + */ + llm: { + new (): { + /** + * Known handles for this diagnostics. + * @type {Diagnostic.Handles} + */ + handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * A container for handles related to the diagnostics + */ + Handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + } + /** + * A container for various filesystem diagnostics. + */ + export class FSDiagnostic extends Diagnostic { + /** + * A container for filesystem watcher diagnostics. + */ + static WatchersDiagnostic: { + new (): { + /** + * Known handles for this diagnostics. + * @type {Diagnostic.Handles} + */ + handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * A container for handles related to the diagnostics + */ + Handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * A container for filesystem descriptors diagnostics. + */ + static DescriptorsDiagnostic: { + new (): { + /** + * Known handles for this diagnostics. + * @type {Diagnostic.Handles} + */ + handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * A container for handles related to the diagnostics + */ + Handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * Known FS watcher diagnostics. + * @type {FSDiagnostic.WatchersDiagnostic} + */ + watchers: { + new (): { + /** + * Known handles for this diagnostics. + * @type {Diagnostic.Handles} + */ + handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * A container for handles related to the diagnostics + */ + Handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * @type {FSDiagnostic.DescriptorsDiagnostic} + */ + descriptors: { + new (): { + /** + * Known handles for this diagnostics. + * @type {Diagnostic.Handles} + */ + handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * A container for handles related to the diagnostics + */ + Handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + } + /** + * A container for various timers diagnostics. + */ + export class TimersDiagnostic extends Diagnostic { + /** + * A container for core timeout timer diagnostics. + */ + static TimeoutDiagnostic: { + new (): { + /** + * Known handles for this diagnostics. + * @type {Diagnostic.Handles} + */ + handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * A container for handles related to the diagnostics + */ + Handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * A container for core interval timer diagnostics. + */ + static IntervalDiagnostic: { + new (): { + /** + * Known handles for this diagnostics. + * @type {Diagnostic.Handles} + */ + handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * A container for handles related to the diagnostics + */ + Handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * A container for core immediate timer diagnostics. + */ + static ImmediateDiagnostic: { + new (): { + /** + * Known handles for this diagnostics. + * @type {Diagnostic.Handles} + */ + handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * A container for handles related to the diagnostics + */ + Handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * @type {TimersDiagnostic.TimeoutDiagnostic} + */ + timeout: { + new (): { + /** + * Known handles for this diagnostics. + * @type {Diagnostic.Handles} + */ + handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * A container for handles related to the diagnostics + */ + Handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * @type {TimersDiagnostic.IntervalDiagnostic} + */ + interval: { + new (): { + /** + * Known handles for this diagnostics. + * @type {Diagnostic.Handles} + */ + handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * A container for handles related to the diagnostics + */ + Handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * @type {TimersDiagnostic.ImmediateDiagnostic} + */ + immediate: { + new (): { + /** + * Known handles for this diagnostics. + * @type {Diagnostic.Handles} + */ + handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + /** + * A container for handles related to the diagnostics + */ + Handles: { + new (): { + /** + * The nunmber of handles in this diagnostics. + * @type {number} + */ + count: number; + /** + * A set of known handle IDs + * @type {string[]} + */ + ids: string[]; + }; + }; + }; + } + /** + * A container for UDP diagnostics. + */ + export class UDPDiagnostic extends Diagnostic { + } + /** + * A container for various queried runtime diagnostics. + */ + export class QueryDiagnostic { + posts: PostsDiagnostic; + childProcess: ChildProcessDiagnostic; + ai: AIDiagnostic; + fs: FSDiagnostic; + timers: TimersDiagnostic; + udp: UDPDiagnostic; + uv: UVDiagnostic; + } + namespace _default { + export { query }; + } + export default _default; +} +declare module "diagnostics/index" { + /** + * @param {string} name + * @return {import('./channels.js').Channel} + */ + export function channel(name: string): import("diagnostics/channels").Channel; + export default exports; + import * as exports from "diagnostics/index"; + import channels from "diagnostics/channels"; + import window from "diagnostics/window"; + import runtime from "diagnostics/runtime"; + namespace ___home_werle_repos_socketsupply_socket_api_diagnostics_index_ { } + export { channels, window, runtime }; +} +declare module "diagnostics" { + export * from "diagnostics/index"; + export default exports; + import * as exports from "diagnostics/index"; +} +declare module "fs/stats" { + /** + * A container for various stats about a file or directory. + */ + export class Stats { + /** + * Creates a `Stats` instance from input, optionally with `BigInt` data types + * @param {object|Stats} [stat] + * @param {fromBigInt=} [fromBigInt = false] + * @return {Stats} + */ + static from(stat?: object | Stats, fromBigInt?: any | undefined): Stats; + /** + * `Stats` class constructor. + * @param {object|Stats} stat + */ + constructor(stat: object | Stats); + dev: any; + ino: any; + mode: any; + nlink: any; + uid: any; + gid: any; + rdev: any; + size: any; + blksize: any; + blocks: any; + atimeMs: any; + mtimeMs: any; + ctimeMs: any; + birthtimeMs: any; + atime: Date; + mtime: Date; + ctime: Date; + birthtime: Date; + /** + * Returns `true` if stats represents a directory. + * @return {Boolean} + */ + isDirectory(): boolean; + /** + * Returns `true` if stats represents a file. + * @return {Boolean} + */ + isFile(): boolean; + /** + * Returns `true` if stats represents a block device. + * @return {Boolean} + */ + isBlockDevice(): boolean; + /** + * Returns `true` if stats represents a character device. + * @return {Boolean} + */ + isCharacterDevice(): boolean; + /** + * Returns `true` if stats represents a symbolic link. + * @return {Boolean} + */ + isSymbolicLink(): boolean; + /** + * Returns `true` if stats represents a FIFO. + * @return {Boolean} + */ + isFIFO(): boolean; + /** + * Returns `true` if stats represents a socket. + * @return {Boolean} + */ + isSocket(): boolean; + } + export default exports; + import * as exports from "fs/stats"; + namespace ___home_werle_repos_socketsupply_socket_api_fs_stats_ { } +} +declare module "fs/fds" { + const _default: { + types: Map; + fds: Map; + ids: Map; + readonly size: number; + get(id: any): any; + syncOpenDescriptors(): Promise; + set(id: any, fd: any, type: any): void; + has(id: any): boolean; + fd(id: any): any; + id(fd: any): any; + release(id: any, closeDescriptor?: boolean): Promise; + retain(id: any): Promise; + delete(id: any): void; + clear(): void; + typeof(id: any): any; + entries(): IterableIterator<[any, any]>; + }; + export default _default; +} +declare module "fs/handle" { + export const kOpening: unique symbol; + export const kClosing: unique symbol; + export const kClosed: unique symbol; + /** + * A container for a descriptor tracked in `fds` and opened in the native layer. + * This class implements the Node.js `FileHandle` interface + * @see {@link https://nodejs.org/dist/latest-v20.x/docs/api/fs.html#class-filehandle} + */ + export class FileHandle extends EventEmitter { + static get DEFAULT_ACCESS_MODE(): number; + static get DEFAULT_OPEN_FLAGS(): string; + static get DEFAULT_OPEN_MODE(): number; + /** + * Creates a `FileHandle` from a given `id` or `fd` + * @param {string|number|FileHandle|object|FileSystemFileHandle} id + * @return {FileHandle} + */ + static from(id: string | number | FileHandle | object | FileSystemFileHandle): FileHandle; + /** + * Determines if access to `path` for `mode` is possible. + * @param {string} path + * @param {number} [mode = 0o666] + * @param {object=} [options] + * @return {Promise} + */ + static access(path: string, mode?: number, options?: object | undefined): Promise; + /** + * Asynchronously open a file. + * @see {@link https://nodejs.org/dist/latest-v20.x/docs/api/fs.html#fspromisesopenpath-flags-mode} + * @param {string | Buffer | URL} path + * @param {string=} [flags = 'r'] + * @param {string|number=} [mode = 0o666] + * @param {object=} [options] + * @return {Promise} + */ + static open(path: string | Buffer | URL, flags?: string | undefined, mode?: (string | number) | undefined, options?: object | undefined): Promise; + /** + * `FileHandle` class constructor + * @ignore + * @param {object} options + */ + constructor(options: object); + flags: number; + path: any; + mode: any; + id: string; + fd: any; + /** + * `true` if the `FileHandle` instance has been opened. + * @type {boolean} + */ + get opened(): boolean; + /** + * `true` if the `FileHandle` is opening. + * @type {boolean} + */ + get opening(): boolean; + /** + * `true` if the `FileHandle` is closing. + * @type {boolean} + */ + get closing(): boolean; + /** + * `true` if the `FileHandle` is closed. + */ + get closed(): boolean; + /** + * Appends to a file, if handle was opened with `O_APPEND`, otherwise this + * method is just an alias to `FileHandle#writeFile()`. + * @param {string|Buffer|TypedArray|Array} data + * @param {object=} [options] + * @param {string=} [options.encoding = 'utf8'] + * @param {object=} [options.signal] + */ + appendFile(data: string | Buffer | TypedArray | any[], options?: object | undefined): Promise; + /** + * Change permissions of file handle. + * @param {number} mode + * @param {object=} [options] + */ + chmod(mode: number, options?: object | undefined): Promise; + /** + * Change ownership of file handle. + * @param {number} uid + * @param {number} gid + * @param {object=} [options] + */ + chown(uid: number, gid: number, options?: object | undefined): Promise; + /** + * Close underlying file handle + * @param {object=} [options] + */ + close(options?: object | undefined): Promise; + /** + * Creates a `ReadStream` for the underlying file. + * @param {object=} [options] - An options object + */ + createReadStream(options?: object | undefined): ReadStream; + /** + * Creates a `WriteStream` for the underlying file. + * @param {object=} [options] - An options object + */ + createWriteStream(options?: object | undefined): WriteStream; + /** + * @param {object=} [options] + */ + datasync(): Promise; + /** + * Opens the underlying descriptor for the file handle. + * @param {object=} [options] + */ + open(options?: object | undefined): Promise; + /** + * Reads `length` bytes starting from `position` into `buffer` at + * `offset`. + * @param {Buffer|object} buffer + * @param {number=} [offset] + * @param {number=} [length] + * @param {number=} [position] + * @param {object=} [options] + */ + read(buffer: Buffer | object, offset?: number | undefined, length?: number | undefined, position?: number | undefined, options?: object | undefined): Promise<{ + bytesRead: number; + buffer: any; + }>; + /** + * Reads the entire contents of a file and returns it as a buffer or a string + * specified of a given encoding specified at `options.encoding`. + * @param {object=} [options] + * @param {string=} [options.encoding = 'utf8'] + * @param {object=} [options.signal] + */ + readFile(options?: object | undefined): Promise; + /** + * Returns the stats of the underlying file. + * @param {object=} [options] + * @return {Promise} + */ + stat(options?: object | undefined): Promise; + /** + * Returns the stats of the underlying symbolic link. + * @param {object=} [options] + * @return {Promise} + */ + lstat(options?: object | undefined): Promise; + /** + * Synchronize a file's in-core state with storage device + * @return {Promise} + */ + sync(): Promise; + /** + * @param {number} [offset = 0] + * @return {Promise} + */ + truncate(offset?: number): Promise; + /** + * Writes `length` bytes at `offset` in `buffer` to the underlying file + * at `position`. + * @param {Buffer|object} buffer + * @param {number} offset + * @param {number} length + * @param {number} position + * @param {object=} [options] + */ + write(buffer: Buffer | object, offset: number, length: number, position: number, options?: object | undefined): Promise; + /** + * Writes `data` to file. + * @param {string|Buffer|TypedArray|Array} data + * @param {object=} [options] + * @param {string=} [options.encoding = 'utf8'] + * @param {object=} [options.signal] + */ + writeFile(data: string | Buffer | TypedArray | any[], options?: object | undefined): Promise; + [exports.kOpening]: any; + [exports.kClosing]: any; + [exports.kClosed]: boolean; + #private; + } + /** + * A container for a directory handle tracked in `fds` and opened in the + * native layer. + */ + export class DirectoryHandle extends EventEmitter { + /** + * The max number of entries that can be bufferd with the `bufferSize` + * option. + */ + static get MAX_BUFFER_SIZE(): number; + static get MAX_ENTRIES(): number; + /** + * The default number of entries `Dirent` that are buffered + * for each read request. + */ + static get DEFAULT_BUFFER_SIZE(): number; + /** + * Creates a `DirectoryHandle` from a given `id` or `fd` + * @param {string|number|DirectoryHandle|object|FileSystemDirectoryHandle} id + * @param {object} options + * @return {DirectoryHandle} + */ + static from(id: string | number | DirectoryHandle | object | FileSystemDirectoryHandle, options: object): DirectoryHandle; + /** + * Asynchronously open a directory. + * @param {string | Buffer | URL} path + * @param {object=} [options] + * @return {Promise} + */ + static open(path: string | Buffer | URL, options?: object | undefined): Promise; + /** + * `DirectoryHandle` class constructor + * @private + * @param {object} options + */ + private constructor(); + id: string; + path: any; + bufferSize: number; + /** + * DirectoryHandle file descriptor id + */ + get fd(): string; + /** + * `true` if the `DirectoryHandle` instance has been opened. + * @type {boolean} + */ + get opened(): boolean; + /** + * `true` if the `DirectoryHandle` is opening. + * @type {boolean} + */ + get opening(): boolean; + /** + * `true` if the `DirectoryHandle` is closing. + * @type {boolean} + */ + get closing(): boolean; + /** + * `true` if `DirectoryHandle` is closed. + */ + get closed(): boolean; + /** + * Opens the underlying handle for a directory. + * @param {object=} options + * @return {Promise} + */ + open(options?: object | undefined): Promise; + /** + * Close underlying directory handle + * @param {object=} [options] + */ + close(options?: object | undefined): Promise; + /** + * Reads directory entries + * @param {object=} [options] + * @param {number=} [options.entries = DirectoryHandle.MAX_ENTRIES] + */ + read(options?: object | undefined): Promise; + [exports.kOpening]: any; + [exports.kClosing]: any; + [exports.kClosed]: boolean; + #private; + } + export default exports; + export type TypedArray = Uint8Array | Int8Array; + import { EventEmitter } from "events"; + import { Buffer } from "buffer"; + import { ReadStream } from "fs/stream"; + import { WriteStream } from "fs/stream"; + import { Stats } from "fs/stats"; + import * as exports from "fs/handle"; + namespace ___home_werle_repos_socketsupply_socket_api_fs_handle_ { } +} +declare module "fs/dir" { + /** + * Sorts directory entries + * @param {string|Dirent} a + * @param {string|Dirent} b + * @return {number} + */ + export function sortDirectoryEntries(a: string | Dirent, b: string | Dirent): number; + export const kType: unique symbol; + /** + * A containerr for a directory and its entries. This class supports scanning + * a directory entry by entry with a `read()` method. The `Symbol.asyncIterator` + * interface is exposed along with an AsyncGenerator `entries()` method. + * @see {@link https://nodejs.org/dist/latest-v20.x/docs/api/fs.html#class-fsdir} + */ + export class Dir { + static from(fdOrHandle: any, options: any): exports.Dir; + /** + * `Dir` class constructor. + * @param {DirectoryHandle} handle + * @param {object=} options + */ + constructor(handle: DirectoryHandle, options?: object | undefined); + path: any; + handle: DirectoryHandle; + encoding: any; + withFileTypes: boolean; + /** + * `true` if closed, otherwise `false`. + * @ignore + * @type {boolean} + */ + get closed(): boolean; + /** + * `true` if closing, otherwise `false`. + * @ignore + * @type {boolean} + */ + get closing(): boolean; + /** + * Closes container and underlying handle. + * @param {object|function} options + * @param {function=} callback + */ + close(options?: object | Function, callback?: Function | undefined): Promise; + /** + * Closes container and underlying handle + * synchronously. + * @param {object=} [options] + */ + closeSync(options?: object | undefined): void; + /** + * Reads and returns directory entry. + * @param {object|function} options + * @param {function=} callback + * @return {Promise} + */ + read(options: object | Function, callback?: Function | undefined): Promise; + /** + * Reads and returns directory entry synchronously. + * @param {object|function} options + * @return {Dirent[]|string[]} + */ + readSync(options?: object | Function): Dirent[] | string[]; + /** + * AsyncGenerator which yields directory entries. + * @param {object=} options + */ + entries(options?: object | undefined): AsyncGenerator; + /** + * `for await (...)` AsyncGenerator support. + */ + get [Symbol.asyncIterator](): (options?: object | undefined) => AsyncGenerator; + } + /** + * A container for a directory entry. + * @see {@link https://nodejs.org/dist/latest-v20.x/docs/api/fs.html#class-fsdirent} + */ + export class Dirent { + static get UNKNOWN(): number; + static get FILE(): number; + static get DIR(): number; + static get LINK(): number; + static get FIFO(): number; + static get SOCKET(): number; + static get CHAR(): number; + static get BLOCK(): number; + /** + * Creates `Dirent` instance from input. + * @param {object|string} name + * @param {(string|number)=} type + */ + static from(name: object | string, type?: (string | number) | undefined): exports.Dirent; + /** + * `Dirent` class constructor. + * @param {string} name + * @param {string|number} type + */ + constructor(name: string, type: string | number); + name: string; + /** + * Read only type. + */ + get type(): number; + /** + * `true` if `Dirent` instance is a directory. + */ + isDirectory(): boolean; + /** + * `true` if `Dirent` instance is a file. + */ + isFile(): boolean; + /** + * `true` if `Dirent` instance is a block device. + */ + isBlockDevice(): boolean; + /** + * `true` if `Dirent` instance is a character device. + */ + isCharacterDevice(): boolean; + /** + * `true` if `Dirent` instance is a symbolic link. + */ + isSymbolicLink(): boolean; + /** + * `true` if `Dirent` instance is a FIFO. + */ + isFIFO(): boolean; + /** + * `true` if `Dirent` instance is a socket. + */ + isSocket(): boolean; + [exports.kType]: number; + } + export default exports; + import { DirectoryHandle } from "fs/handle"; + import * as exports from "fs/dir"; + namespace ___home_werle_repos_socketsupply_socket_api_fs_dir_ { } +} +declare module "hooks" { + /** + * Wait for a hook event to occur. + * @template {Event | T extends Event} + * @param {string|function} nameOrFunction + * @return {Promise} + */ + export function wait(nameOrFunction: string | Function): Promise; + /** + * Wait for the global Window, Document, and Runtime to be ready. + * The callback function is called exactly once. + * @param {function} callback + * @return {function} + */ + export function onReady(callback: Function): Function; + /** + * Wait for the global Window and Document to be ready. The callback + * function is called exactly once. + * @param {function} callback + * @return {function} + */ + export function onLoad(callback: Function): Function; + /** + * Wait for the runtime to be ready. The callback + * function is called exactly once. + * @param {function} callback + * @return {function} + */ + export function onInit(callback: Function): Function; + /** + * Calls callback when a global exception occurs. + * 'error', 'messageerror', and 'unhandledrejection' events are handled here. + * @param {function} callback + * @return {function} + */ + export function onError(callback: Function): Function; + /** + * Subscribes to the global data pipe calling callback when + * new data is emitted on the global Window. + * @param {function} callback + * @return {function} + */ + export function onData(callback: Function): Function; + /** + * Subscribes to global messages likely from an external `postMessage` + * invocation. + * @param {function} callback + * @return {function} + */ + export function onMessage(callback: Function): Function; + /** + * Calls callback when runtime is working online. + * @param {function} callback + * @return {function} + */ + export function onOnline(callback: Function): Function; + /** + * Calls callback when runtime is not working online. + * @param {function} callback + * @return {function} + */ + export function onOffline(callback: Function): Function; + /** + * Calls callback when runtime user preferred language has changed. + * @param {function} callback + * @return {function} + */ + export function onLanguageChange(callback: Function): Function; + /** + * Calls callback when an application permission has changed. + * @param {function} callback + * @return {function} + */ + export function onPermissionChange(callback: Function): Function; + /** + * Calls callback in response to a presented `Notification`. + * @param {function} callback + * @return {function} + */ + export function onNotificationResponse(callback: Function): Function; + /** + * Calls callback when a `Notification` is presented. + * @param {function} callback + * @return {function} + */ + export function onNotificationPresented(callback: Function): Function; + /** + * Calls callback when a `ApplicationURL` is opened. + * @param {function(ApplicationURLEvent)} callback + * @return {function} + */ + export function onApplicationURL(callback: (arg0: ApplicationURLEvent) => any): Function; + /** + * Calls callback when a `ApplicationPause` is dispatched. + * @param {function} callback + * @return {function} + */ + export function onApplicationPause(callback: Function): Function; + /** + * Calls callback when a `ApplicationResume` is dispatched. + * @param {function} callback + * @return {function} + */ + export function onApplicationResume(callback: Function): Function; + export const RUNTIME_INIT_EVENT_NAME: "__runtime_init__"; + export const GLOBAL_EVENTS: string[]; + /** + * An event dispatched when the runtime has been initialized. + */ + export class InitEvent { + constructor(); + } + /** + * An event dispatched when the runtime global has been loaded. + */ + export class LoadEvent { + constructor(); + } + /** + * An event dispatched when the runtime is considered ready. + */ + export class ReadyEvent { + constructor(); + } + /** + * An event dispatched when the runtime has been initialized. + */ + export class RuntimeInitEvent { + constructor(); + } + /** + * An interface for registering callbacks for various hooks in + * the runtime. + */ + export class Hooks extends EventTarget { + /** + * @ignore + */ + static GLOBAL_EVENTS: string[]; + /** + * @ignore + */ + static InitEvent: typeof InitEvent; + /** + * @ignore + */ + static LoadEvent: typeof LoadEvent; + /** + * @ignore + */ + static ReadyEvent: typeof ReadyEvent; + /** + * @ignore + */ + static RuntimeInitEvent: typeof RuntimeInitEvent; + /** + * An array of all global events listened to in various hooks + */ + get globalEvents(): string[]; + /** + * Reference to global object + * @type {object} + */ + get global(): any; + /** + * Returns `document` in global. + * @type {Document} + */ + get document(): Document; + /** + * Returns `document` in global. + * @type {Window} + */ + get window(): Window; + /** + * Predicate for determining if the global document is ready. + * @type {boolean} + */ + get isDocumentReady(): boolean; + /** + * Predicate for determining if the global object is ready. + * @type {boolean} + */ + get isGlobalReady(): boolean; + /** + * Predicate for determining if the runtime is ready. + * @type {boolean} + */ + get isRuntimeReady(): boolean; + /** + * Predicate for determining if everything is ready. + * @type {boolean} + */ + get isReady(): boolean; + /** + * Predicate for determining if the runtime is working online. + * @type {boolean} + */ + get isOnline(): boolean; + /** + * Predicate for determining if the runtime is in a Worker context. + * @type {boolean} + */ + get isWorkerContext(): boolean; + /** + * Predicate for determining if the runtime is in a Window context. + * @type {boolean} + */ + get isWindowContext(): boolean; + /** + * Wait for a hook event to occur. + * @template {Event | T extends Event} + * @param {string|function} nameOrFunction + * @param {WaitOptions=} [options] + * @return {Promise} + */ + wait(nameOrFunction: string | Function, options?: WaitOptions | undefined): Promise; + /** + * Wait for the global Window, Document, and Runtime to be ready. + * The callback function is called exactly once. + * @param {function} callback + * @return {function} + */ + onReady(callback: Function): Function; + /** + * Wait for the global Window and Document to be ready. The callback + * function is called exactly once. + * @param {function} callback + * @return {function} + */ + onLoad(callback: Function): Function; + /** + * Wait for the runtime to be ready. The callback + * function is called exactly once. + * @param {function} callback + * @return {function} + */ + onInit(callback: Function): Function; + /** + * Calls callback when a global exception occurs. + * 'error', 'messageerror', and 'unhandledrejection' events are handled here. + * @param {function} callback + * @return {function} + */ + onError(callback: Function): Function; + /** + * Subscribes to the global data pipe calling callback when + * new data is emitted on the global Window. + * @param {function} callback + * @return {function} + */ + onData(callback: Function): Function; + /** + * Subscribes to global messages likely from an external `postMessage` + * invocation. + * @param {function} callback + * @return {function} + */ + onMessage(callback: Function): Function; + /** + * Calls callback when runtime is working online. + * @param {function} callback + * @return {function} + */ + onOnline(callback: Function): Function; + /** + * Calls callback when runtime is not working online. + * @param {function} callback + * @return {function} + */ + onOffline(callback: Function): Function; + /** + * Calls callback when runtime user preferred language has changed. + * @param {function} callback + * @return {function} + */ + onLanguageChange(callback: Function): Function; + /** + * Calls callback when an application permission has changed. + * @param {function} callback + * @return {function} + */ + onPermissionChange(callback: Function): Function; + /** + * Calls callback in response to a displayed `Notification`. + * @param {function} callback + * @return {function} + */ + onNotificationResponse(callback: Function): Function; + /** + * Calls callback when a `Notification` is presented. + * @param {function} callback + * @return {function} + */ + onNotificationPresented(callback: Function): Function; + /** + * Calls callback when a `ApplicationURL` is opened. + * @param {function} callback + * @return {function} + */ + onApplicationURL(callback: Function): Function; + /** + * Calls callback when an `ApplicationPause` is dispatched. + * @param {function} callback + * @return {function} + */ + onApplicationPause(callback: Function): Function; + /** + * Calls callback when an `ApplicationResume` is dispatched. + * @param {function} callback + * @return {function} + */ + onApplicationResume(callback: Function): Function; + #private; + } + export default hooks; + export type WaitOptions = { + signal?: AbortSignal; + }; + /** + * `Hooks` single instance. + * @ignore + */ + const hooks: Hooks; +} +declare module "fs/watcher" { + /** + * A container for a file system path watcher. + */ + export class Watcher extends EventEmitter { + /** + * `Watcher` class constructor. + * @ignore + * @param {string} path + * @param {object=} [options] + * @param {AbortSignal=} [options.signal} + * @param {string|number|bigint=} [options.id] + * @param {string=} [options.encoding = 'utf8'] + */ + constructor(path: string, options?: object | undefined); + /** + * The underlying `fs.Watcher` resource id. + * @ignore + * @type {string} + */ + id: string; + /** + * The path the `fs.Watcher` is watching + * @type {string} + */ + path: string; + /** + * `true` if closed, otherwise `false. + * @type {boolean} + */ + closed: boolean; + /** + * `true` if aborted, otherwise `false`. + * @type {boolean} + */ + aborted: boolean; + /** + * The encoding of the `filename` + * @type {'utf8'|'buffer'} + */ + encoding: "utf8" | "buffer"; + /** + * A `AbortController` `AbortSignal` for async aborts. + * @type {AbortSignal?} + */ + signal: AbortSignal | null; + /** + * Internal event listener cancellation. + * @ignore + * @type {function?} + */ + stopListening: Function | null; + /** + * Internal starter for watcher. + * @ignore + */ + start(): Promise; + /** + * Closes watcher and stops listening for changes. + * @return {Promise} + */ + close(): Promise; + /** + * Implements the `AsyncIterator` (`Symbol.asyncIterator`) iterface. + * @ignore + * @return {AsyncIterator<{ eventType: string, filename: string }>} + */ + [Symbol.asyncIterator](): AsyncIterator<{ + eventType: string; + filename: string; + }>; + #private; + } + export default Watcher; + import { EventEmitter } from "events"; +} +declare module "fs/promises" { + /** + * Asynchronously check access a file. + * @see {@link https://nodejs.org/dist/latest-v20.x/docs/api/fs.html#fspromisesaccesspath-mode} + * @param {string|Buffer|URL} path + * @param {number=} [mode] + * @param {object=} [options] + */ + export function access(path: string | Buffer | URL, mode?: number | undefined, options?: object | undefined): Promise; + /** + * @see {@link https://nodejs.org/api/fs.html#fspromiseschmodpath-mode} + * @param {string | Buffer | URL} path + * @param {number} mode + * @returns {Promise} + */ + export function chmod(path: string | Buffer | URL, mode: number): Promise; + /** + * Changes ownership of file or directory at `path` with `uid` and `gid`. + * @param {string} path + * @param {number} uid + * @param {number} gid + * @return {Promise} + */ + export function chown(path: string, uid: number, gid: number): Promise; + /** + * Asynchronously copies `src` to `dest` calling `callback` upon success or error. + * @param {string} src - The source file path. + * @param {string} dest - The destination file path. + * @param {number} flags - Modifiers for copy operation. + * @return {Promise} + */ + export function copyFile(src: string, dest: string, flags?: number): Promise; + /** + * Chages ownership of link at `path` with `uid` and `gid. + * @param {string} path + * @param {number} uid + * @param {number} gid + * @return {Promise} + */ + export function lchown(path: string, uid: number, gid: number): Promise; + /** + * Creates a link to `dest` from `dest`. + * @param {string} src + * @param {string} dest + * @return {Promise} + */ + export function link(src: string, dest: string): Promise; + /** + * Asynchronously creates a directory. + * + * @param {string} path - The path to create + * @param {object} [options] - The optional options argument can be an integer specifying mode (permission and sticky bits), or an object with a mode property and a recursive property indicating whether parent directories should be created. Calling fs.mkdir() when path is a directory that exists results in an error only when recursive is false. + * @param {boolean} [options.recursive=false] - Recursively create missing path segments. + * @param {number} [options.mode=0o777] - Set the mode of directory, or missing path segments when recursive is true. + * @return {Promise} - Upon success, fulfills with undefined if recursive is false, or the first directory path created if recursive is true. + */ + export function mkdir(path: string, options?: { + recursive?: boolean; + mode?: number; + }): Promise; + /** + * Asynchronously open a file. + * @see {@link https://nodejs.org/api/fs.html#fspromisesopenpath-flags-mode } + * + * @param {string | Buffer | URL} path + * @param {string=} flags - default: 'r' + * @param {number=} mode - default: 0o666 + * @return {Promise} + */ + export function open(path: string | Buffer | URL, flags?: string | undefined, mode?: number | undefined): Promise; + /** + * @see {@link https://nodejs.org/api/fs.html#fspromisesopendirpath-options} + * @param {string | Buffer | URL} path + * @param {object=} [options] + * @param {string=} [options.encoding = 'utf8'] + * @param {number=} [options.bufferSize = 32] + * @return {Promise} + */ + export function opendir(path: string | Buffer | URL, options?: object | undefined): Promise; + /** + * @see {@link https://nodejs.org/dist/latest-v20.x/docs/api/fs.html#fspromisesreaddirpath-options} + * @param {string|Buffer|URL} path + * @param {object=} [options] + * @param {string=} [options.encoding = 'utf8'] + * @param {boolean=} [options.withFileTypes = false] + * @return {Promise<(string|Dirent)[]>} + */ + export function readdir(path: string | Buffer | URL, options?: object | undefined): Promise<(string | Dirent)[]>; + /** + * @see {@link https://nodejs.org/dist/latest-v20.x/docs/api/fs.html#fspromisesreadfilepath-options} + * @param {string} path + * @param {object=} [options] + * @param {(string|null)=} [options.encoding = null] + * @param {string=} [options.flag = 'r'] + * @param {AbortSignal|undefined} [options.signal] + * @return {Promise} + */ + export function readFile(path: string, options?: object | undefined): Promise; + /** + * Reads link at `path` + * @param {string} path + * @return {Promise} + */ + export function readlink(path: string): Promise; + /** + * Computes real path for `path` + * @param {string} path + * @return {Promise} + */ + export function realpath(path: string): Promise; + /** + * Renames file or directory at `src` to `dest`. + * @param {string} src + * @param {string} dest + * @return {Promise} + */ + export function rename(src: string, dest: string): Promise; + /** + * Removes directory at `path`. + * @param {string} path + * @return {Promise} + */ + export function rmdir(path: string): Promise; + /** + * Get the stats of a file + * @see {@link https://nodejs.org/api/fs.html#fspromisesstatpath-options} + * @param {string | Buffer | URL} path + * @param {object=} [options] + * @param {boolean=} [options.bigint = false] + * @return {Promise} + */ + export function stat(path: string | Buffer | URL, options?: object | undefined): Promise; + /** + * Get the stats of a symbolic link. + * @see {@link https://nodejs.org/api/fs.html#fspromiseslstatpath-options} + * @param {string | Buffer | URL} path + * @param {object=} [options] + * @param {boolean=} [options.bigint = false] + * @return {Promise} + */ + export function lstat(path: string | Buffer | URL, options?: object | undefined): Promise; + /** + * Creates a symlink of `src` at `dest`. + * @param {string} src + * @param {string} dest + * @return {Promise} + */ + export function symlink(src: string, dest: string, type?: any): Promise; + /** + * Unlinks (removes) file at `path`. + * @param {string} path + * @return {Promise} + */ + export function unlink(path: string): Promise; + /** + * @see {@link https://nodejs.org/dist/latest-v20.x/docs/api/fs.html#fspromiseswritefilefile-data-options} + * @param {string|Buffer|URL|FileHandle} path - filename or FileHandle + * @param {string|Buffer|Array|DataView|TypedArray} data + * @param {object=} [options] + * @param {(string|null)=} [options.encoding = 'utf8'] + * @param {number=} [options.mode = 0o666] + * @param {string=} [options.flag = 'w'] + * @param {AbortSignal|undefined} [options.signal] + * @return {Promise} + */ + export function writeFile(path: string | Buffer | URL | FileHandle, data: string | Buffer | any[] | DataView | TypedArray, options?: object | undefined): Promise; + /** + * Watch for changes at `path` calling `callback` + * @param {string} + * @param {function|object=} [options] + * @param {string=} [options.encoding = 'utf8'] + * @param {AbortSignal=} [options.signal] + * @return {Watcher} + */ + export function watch(path: any, options?: (Function | object) | undefined): Watcher; + export type Stats = any; + export default exports; + export type Buffer = import("buffer").Buffer; + export type TypedArray = Uint8Array | Int8Array; + import { FileHandle } from "fs/handle"; + import { Dir } from "fs/dir"; + import { Dirent } from "fs/dir"; + import { Stats } from "fs/stats"; + import { Watcher } from "fs/watcher"; + import bookmarks from "fs/bookmarks"; + import * as constants from "fs/constants"; + import { DirectoryHandle } from "fs/handle"; + import fds from "fs/fds"; + import { ReadStream } from "fs/stream"; + import { WriteStream } from "fs/stream"; + import * as exports from "fs/promises"; + namespace ___home_werle_repos_socketsupply_socket_api_fs_promises_ { } + export { bookmarks, constants, Dir, DirectoryHandle, Dirent, fds, FileHandle, ReadStream, Watcher, WriteStream }; +} +declare module "fs/index" { + /** + * Asynchronously check access a file for a given mode calling `callback` + * upon success or error. + * @see {@link https://nodejs.org/api/fs.html#fsopenpath-flags-mode-callback} + * @param {string | Buffer | URL} path + * @param {number?|function(Error|null):any?} [mode = F_OK(0)] + * @param {function(Error|null):any?} [callback] + */ + export function access(path: string | Buffer | URL, mode: any, callback?: (arg0: Error | null) => any | null): void; + /** + * Synchronously check access a file for a given mode calling `callback` + * upon success or error. + * @see {@link https://nodejs.org/api/fs.html#fsopenpath-flags-mode-callback} + * @param {string | Buffer | URL} path + * @param {string?} [mode = F_OK(0)] + */ + export function accessSync(path: string | Buffer | URL, mode?: string | null): boolean; + /** + * Checks if a path exists + * @param {string | Buffer | URL} path + * @param {function(Boolean)?} [callback] + */ + export function exists(path: string | Buffer | URL, callback?: ((arg0: boolean) => any) | null): void; + /** + * Checks if a path exists + * @param {string | Buffer | URL} path + * @param {function(Boolean)?} [callback] + */ + export function existsSync(path: string | Buffer | URL): boolean; + /** + * Asynchronously changes the permissions of a file. + * No arguments other than a possible exception are given to the completion callback + * + * @see {@link https://nodejs.org/api/fs.html#fschmodpath-mode-callback} + * + * @param {string | Buffer | URL} path + * @param {number} mode + * @param {function(Error?)} callback + */ + export function chmod(path: string | Buffer | URL, mode: number, callback: (arg0: Error | null) => any): TypeError; + /** + * Synchronously changes the permissions of a file. + * + * @see {@link https://nodejs.org/api/fs.html#fschmodpath-mode-callback} + * @param {string | Buffer | URL} path + * @param {number} mode + */ + export function chmodSync(path: string | Buffer | URL, mode: number): void; + /** + * Changes ownership of file or directory at `path` with `uid` and `gid`. + * @param {string} path + * @param {number} uid + * @param {number} gid + * @param {function} callback + */ + export function chown(path: string, uid: number, gid: number, callback: Function): TypeError; + /** + * Changes ownership of file or directory at `path` with `uid` and `gid`. + * @param {string} path + * @param {number} uid + * @param {number} gid + */ + export function chownSync(path: string, uid: number, gid: number): void; + /** + * Asynchronously close a file descriptor calling `callback` upon success or error. + * @see {@link https://nodejs.org/api/fs.html#fsclosefd-callback} + * @param {number} fd + * @param {function(Error?)?} [callback] + */ + export function close(fd: number, callback?: ((arg0: Error | null) => any) | null): void; + /** + * Synchronously close a file descriptor. + * @param {number} fd - fd + */ + export function closeSync(fd: number): void; + /** + * Asynchronously copies `src` to `dest` calling `callback` upon success or error. + * @param {string} src - The source file path. + * @param {string} dest - The destination file path. + * @param {number} flags - Modifiers for copy operation. + * @param {function(Error=)=} [callback] - The function to call after completion. + * @see {@link https://nodejs.org/api/fs.html#fscopyfilesrc-dest-mode-callback} + */ + export function copyFile(src: string, dest: string, flags?: number, callback?: ((arg0: Error | undefined) => any) | undefined): void; + /** + * Synchronously copies `src` to `dest` calling `callback` upon success or error. + * @param {string} src - The source file path. + * @param {string} dest - The destination file path. + * @param {number} flags - Modifiers for copy operation. + * @see {@link https://nodejs.org/api/fs.html#fscopyfilesrc-dest-mode-callback} + */ + export function copyFileSync(src: string, dest: string, flags?: number): void; + /** + * @see {@link https://nodejs.org/api/fs.html#fscreatewritestreampath-options} + * @param {string | Buffer | URL} path + * @param {object?} [options] + * @returns {ReadStream} + */ + export function createReadStream(path: string | Buffer | URL, options?: object | null): ReadStream; + /** + * @see {@link https://nodejs.org/api/fs.html#fscreatewritestreampath-options} + * @param {string | Buffer | URL} path + * @param {object?} [options] + * @returns {WriteStream} + */ + export function createWriteStream(path: string | Buffer | URL, options?: object | null): WriteStream; + /** + * Invokes the callback with the for the file descriptor. See + * the POSIX fstat(2) documentation for more detail. + * + * @see {@link https://nodejs.org/api/fs.html#fsfstatfd-options-callback} + * + * @param {number} fd - A file descriptor. + * @param {object?|function?} [options] - An options object. + * @param {function?} callback - The function to call after completion. + */ + export function fstat(fd: number, options: any, callback: Function | null): void; + /** + * Request that all data for the open file descriptor is flushed + * to the storage device. + * @param {number} fd - A file descriptor. + * @param {function} callback - The function to call after completion. + */ + export function fsync(fd: number, callback: Function): void; + /** + * Truncates the file up to `offset` bytes. + * @param {number} fd - A file descriptor. + * @param {number=|function} [offset = 0] + * @param {function?} callback - The function to call after completion. + */ + export function ftruncate(fd: number, offset: any, callback: Function | null): void; + /** + * Chages ownership of link at `path` with `uid` and `gid. + * @param {string} path + * @param {number} uid + * @param {number} gid + * @param {function} callback + */ + export function lchown(path: string, uid: number, gid: number, callback: Function): TypeError; + /** + * Creates a link to `dest` from `src`. + * @param {string} src + * @param {string} dest + * @param {function} + */ + export function link(src: string, dest: string, callback: any): void; + /** + * @ignore + */ + export function mkdir(path: any, options: any, callback: any): void; + /** + * @ignore + * @param {string|URL} path + * @param {object=} [options] + */ + export function mkdirSync(path: string | URL, options?: object | undefined): void; + /** + * Asynchronously open a file calling `callback` upon success or error. + * @see {@link https://nodejs.org/api/fs.html#fsopenpath-flags-mode-callback} + * @param {string | Buffer | URL} path + * @param {string=} [flags = 'r'] + * @param {number=} [mode = 0o666] + * @param {(object|function(Error|null, number|undefined):any)=} [options] + * @param {(function(Error|null, number|undefined):any)|null} [callback] + */ + export function open(path: string | Buffer | URL, flags?: string | undefined, mode?: number | undefined, options?: (object | ((arg0: Error | null, arg1: number | undefined) => any)) | undefined, callback?: ((arg0: Error | null, arg1: number | undefined) => any) | null): void; + /** + * Synchronously open a file. + * @param {string|Buffer|URL} path + * @param {string=} [flags = 'r'] + * @param {string=} [mode = 0o666] + * @param {object=} [options] + */ + export function openSync(path: string | Buffer | URL, flags?: string | undefined, mode?: string | undefined, options?: object | undefined): any; + /** + * Asynchronously open a directory calling `callback` upon success or error. + * @see {@link https://nodejs.org/api/fs.html#fsreaddirpath-options-callback} + * @param {string | Buffer | URL} path + * @param {(object|function(Error|Null, Dir|undefined):any)=} [options] + * @param {string=} [options.encoding = 'utf8'] + * @param {boolean=} [options.withFileTypes = false] + * @param {function(Error|null, Dir|undefined):any)} callback + */ + export function opendir(path: string | Buffer | URL, options?: (object | ((arg0: Error | null, arg1: Dir | undefined) => any)) | undefined, callback: any): void; + /** + * Synchronously open a directory. + * @see {@link https://nodejs.org/api/fs.html#fsreaddirpath-options-callback} + * @param {string|Buffer|URL} path + * @param {objec} [options] + * @param {string=} [options.encoding = 'utf8'] + * @param {boolean=} [options.withFileTypes = false] + * @return {Dir} + */ + export function opendirSync(path: string | Buffer | URL, options?: objec): Dir; + /** + * Asynchronously read from an open file descriptor. + * @see {@link https://nodejs.org/api/fs.html#fsreadfd-buffer-offset-length-position-callback} + * @param {number} fd + * @param {object|Buffer|Uint8Array} buffer - The buffer that the data will be written to. + * @param {number} offset - The position in buffer to write the data to. + * @param {number} length - The number of bytes to read. + * @param {number|BigInt|null} position - Specifies where to begin reading from in the file. If position is null or -1 , data will be read from the current file position, and the file position will be updated. If position is an integer, the file position will be unchanged. + * @param {function(Error|null, number|undefined, Buffer|undefined):any} callback + */ + export function read(fd: number, buffer: object | Buffer | Uint8Array, offset: number, length: number, position: number | BigInt | null, options: any, callback: (arg0: Error | null, arg1: number | undefined, arg2: Buffer | undefined) => any): void; + /** + * Asynchronously write to an open file descriptor. + * @see {@link https://nodejs.org/api/fs.html#fswritefd-buffer-offset-length-position-callback} + * @param {number} fd + * @param {object|Buffer|Uint8Array} buffer - The buffer that the data will be written to. + * @param {number} offset - The position in buffer to write the data to. + * @param {number} length - The number of bytes to read. + * @param {number|BigInt|null} position - Specifies where to begin reading from in the file. If position is null or -1 , data will be read from the current file position, and the file position will be updated. If position is an integer, the file position will be unchanged. + * @param {function(Error|null, number|undefined, Buffer|undefined):any} callback + */ + export function write(fd: number, buffer: object | Buffer | Uint8Array, offset: number, length: number, position: number | BigInt | null, options: any, callback: (arg0: Error | null, arg1: number | undefined, arg2: Buffer | undefined) => any): void; + /** + * Asynchronously read all entries in a directory. + * @see {@link https://nodejs.org/api/fs.html#fsreaddirpath-options-callback} + * @param {string|Buffer|URL} path + * @param {object|function(Error|null, (Dirent|string)[]|undefined):any} [options] + * @param {string=} [options.encoding = 'utf8'] + * @param {boolean=} [options.withFileTypes = false] + * @param {function(Error|null, (Dirent|string)[]):any} callback + */ + export function readdir(path: string | Buffer | URL, options?: object | ((arg0: Error | null, arg1: (Dirent | string)[] | undefined) => any), callback: (arg0: Error | null, arg1: (Dirent | string)[]) => any): void; + /** + * Synchronously read all entries in a directory. + * @see {@link https://nodejs.org/api/fs.html#fsreaddirpath-options-callback} + * @param {string|Buffer | URL } path + * @param {object=} [options] + * @param {string=} [options.encoding ? 'utf8'] + * @param {boolean=} [options.withFileTypes ? false] + * @return {(Dirent|string)[]} + */ + export function readdirSync(path: string | Buffer | URL, options?: object | undefined): (Dirent | string)[]; + /** + * @param {string|Buffer|URL|number} path + * @param {object|function(Error|null, Buffer|string|undefined):any} options + * @param {string=} [options.encoding = 'utf8'] + * @param {string=} [options.flag = 'r'] + * @param {AbortSignal|undefined} [options.signal] + * @param {function(Error|null, Buffer|string|undefined):any} callback + */ + export function readFile(path: string | Buffer | URL | number, options: object | ((arg0: Error | null, arg1: Buffer | string | undefined) => any), callback: (arg0: Error | null, arg1: Buffer | string | undefined) => any): void; + /** + * @param {string|Buffer|URL|number} path + * @param {{ encoding?: string = 'utf8', flags?: string = 'r'}} [options] + * @param {object|function(Error|null, Buffer|undefined):any} [options] + * @param {AbortSignal|undefined} [options.signal] + * @return {string|Buffer} + */ + export function readFileSync(path: string | Buffer | URL | number, options?: { + encoding?: string; + flags?: string; + }): string | Buffer; + /** + * Reads link at `path` + * @param {string} path + * @param {function(Error|null, string|undefined):any} callback + */ + export function readlink(path: string, callback: (arg0: Error | null, arg1: string | undefined) => any): void; + /** + * Computes real path for `path` + * @param {string} path + * @param {function(Error|null, string|undefined):any} callback + */ + export function realpath(path: string, callback: (arg0: Error | null, arg1: string | undefined) => any): void; + /** + * Computes real path for `path` + * @param {string} path + * @return {string} + */ + export function realpathSync(path: string): string; + /** + * Renames file or directory at `src` to `dest`. + * @param {string} src + * @param {string} dest + * @param {function(Error|null):any} callback + */ + export function rename(src: string, dest: string, callback: (arg0: Error | null) => any): void; + /** + * Renames file or directory at `src` to `dest`, synchronously. + * @param {string} src + * @param {string} dest + */ + export function renameSync(src: string, dest: string): void; + /** + * Removes directory at `path`. + * @param {string} path + * @param {function(Error|null):any} callback + */ + export function rmdir(path: string, callback: (arg0: Error | null) => any): void; + /** + * Removes directory at `path`, synchronously. + * @param {string} path + */ + export function rmdirSync(path: string): void; + /** + * Synchronously get the stats of a file + * @param {string} path - filename or file descriptor + * @param {object=} [options] + * @param {string=} [options.encoding ? 'utf8'] + * @param {string=} [options.flag ? 'r'] + */ + export function statSync(path: string, options?: object | undefined): promises.Stats; + /** + * Get the stats of a file + * @param {string|Buffer|URL|number} path - filename or file descriptor + * @param {(object|function(Error|null, Stats|undefined):any)=} [options] + * @param {string=} [options.encoding ? 'utf8'] + * @param {string=} [options.flag ? 'r'] + * @param {AbortSignal|undefined} [options.signal] + * @param {function(Error|null, Stats|undefined):any} callback + */ + export function stat(path: string | Buffer | URL | number, options?: (object | ((arg0: Error | null, arg1: Stats | undefined) => any)) | undefined, callback: (arg0: Error | null, arg1: Stats | undefined) => any): void; + /** + * Get the stats of a symbolic link + * @param {string|Buffer|URL|number} path - filename or file descriptor + * @param {(object|function(Error|null, Stats|undefined):any)=} [options] + * @param {string=} [options.encoding ? 'utf8'] + * @param {string=} [options.flag ? 'r'] + * @param {AbortSignal|undefined} [options.signal] + * @param {function(Error|null, Stats|undefined):any} callback + */ + export function lstat(path: string | Buffer | URL | number, options?: (object | ((arg0: Error | null, arg1: Stats | undefined) => any)) | undefined, callback: (arg0: Error | null, arg1: Stats | undefined) => any): void; + /** + * Creates a symlink of `src` at `dest`. + * @param {string} src + * @param {string} dest + * @param {function(Error|null):any} callback + */ + export function symlink(src: string, dest: string, type: any, callback: (arg0: Error | null) => any): void; + /** + * Unlinks (removes) file at `path`. + * @param {string} path + * @param {function(Error|null):any} callback + */ + export function unlink(path: string, callback: (arg0: Error | null) => any): void; + /** + * Unlinks (removes) file at `path`, synchronously. + * @param {string} path + */ + export function unlinkSync(path: string): void; + /** + * @see {@link https://nodejs.org/api/fs.html#fswritefilefile-data-options-callback} + * @param {string|Buffer|URL|number} path - filename or file descriptor + * @param {string|Buffer|TypedArray|DataView|object} data + * @param {(object|function(Error|null):any)=} [options] + * @param {string=} [options.encoding ? 'utf8'] + * @param {string=} [options.mode ? 0o666] + * @param {string=} [options.flag ? 'w'] + * @param {AbortSignal|undefined} [options.signal] + * @param {function(Error|null):any} callback + */ + export function writeFile(path: string | Buffer | URL | number, data: string | Buffer | TypedArray | DataView | object, options?: (object | ((arg0: Error | null) => any)) | undefined, callback: (arg0: Error | null) => any): void; + /** + * Writes data to a file synchronously. + * @param {string|Buffer|URL|number} path - filename or file descriptor + * @param {string|Buffer|TypedArray|DataView|object} data + * @param {object=} [options] + * @param {string=} [options.encoding ? 'utf8'] + * @param {string=} [options.mode ? 0o666] + * @param {string=} [options.flag ? 'w'] + * @param {AbortSignal|undefined} [options.signal] + * @see {@link https://nodejs.org/api/fs.html#fswritefilesyncfile-data-options} + */ + export function writeFileSync(path: string | Buffer | URL | number, data: string | Buffer | TypedArray | DataView | object, options?: object | undefined): void; + /** + * Watch for changes at `path` calling `callback` + * @param {string} + * @param {function|object=} [options] + * @param {string=} [options.encoding = 'utf8'] + * @param {function=} [callback] + * @return {Watcher} + */ + export function watch(path: any, options?: (Function | object) | undefined, callback?: Function | undefined): Watcher; + export default exports; + export type Buffer = import("buffer").Buffer; + export type TypedArray = Uint8Array | Int8Array; + import { Buffer } from "buffer"; + import { ReadStream } from "fs/stream"; + import { WriteStream } from "fs/stream"; + import { Dir } from "fs/dir"; + import { Dirent } from "fs/dir"; + import * as promises from "fs/promises"; + import { Stats } from "fs/stats"; + import { Watcher } from "fs/watcher"; + import bookmarks from "fs/bookmarks"; + import * as constants from "fs/constants"; + import { DirectoryHandle } from "fs/handle"; + import fds from "fs/fds"; + import { FileHandle } from "fs/handle"; + import * as exports from "fs/index"; + namespace ___home_werle_repos_socketsupply_socket_api_fs_index_ { } + export { bookmarks, constants, Dir, DirectoryHandle, Dirent, fds, FileHandle, promises, ReadStream, Stats, Watcher, WriteStream }; +} +declare module "fs" { + export * from "fs/index"; + export default exports; + import * as exports from "fs/index"; +} +declare module "external/libsodium/index" { + const _default: any; + export default _default; +} +declare module "crypto/sodium" { + export {}; +} +declare module "crypto" { + /** + * Generate cryptographically strong random values into the `buffer` + * @param {TypedArray} buffer + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues} + * @return {TypedArray} + */ + export function getRandomValues(buffer: TypedArray, ...args: any[]): TypedArray; + /** + * Generate a random 64-bit number. + * @returns {BigInt} - A random 64-bit number. + */ + export function rand64(): BigInt; + /** + * Generate `size` random bytes. + * @param {number} size - The number of bytes to generate. The size must not be larger than 2**31 - 1. + * @returns {Buffer} - A promise that resolves with an instance of socket.Buffer with random bytes. + */ + export function randomBytes(size: number): Buffer; + /** + * @param {string} algorithm - `SHA-1` | `SHA-256` | `SHA-384` | `SHA-512` + * @param {Buffer | TypedArray | DataView} message - An instance of socket.Buffer, TypedArray or Dataview. + * @returns {Promise} - A promise that resolves with an instance of socket.Buffer with the hash. + */ + export function createDigest(algorithm: string, buf: any): Promise; + /** + * A murmur3 hash implementation based on https://github.com/jwerle/murmurhash.c + * that works on strings and `ArrayBuffer` views (typed arrays) + * @param {string|Uint8Array|ArrayBuffer} value + * @param {number=} [seed = 0] + * @return {number} + */ + export function murmur3(value: string | Uint8Array | ArrayBuffer, seed?: number | undefined): number; + /** + * @typedef {Uint8Array|Int8Array} TypedArray + */ + /** + * WebCrypto API + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Crypto} + */ + export let webcrypto: any; + /** + * A promise that resolves when all internals to be loaded/ready. + * @type {Promise} + */ + export const ready: Promise; + /** + * Maximum total size of random bytes per page + */ + export const RANDOM_BYTES_QUOTA: number; + /** + * Maximum total size for random bytes. + */ + export const MAX_RANDOM_BYTES: 281474976710655; + /** + * Maximum total amount of allocated per page of bytes (max/quota) + */ + export const MAX_RANDOM_BYTES_PAGES: number; + export default exports; + export type TypedArray = Uint8Array | Int8Array; + import { Buffer } from "buffer"; + export namespace sodium { + let ready: Promise; + } + import * as exports from "crypto"; + namespace ___home_werle_repos_socketsupply_socket_api_crypto_ { } +} +declare module "ai" { + /** + * A class to interact with large language models (using llama.cpp) + */ + export class LLM extends EventEmitter { + /** + * Constructs an LLM instance. Each parameter is designed to configure and control + * the behavior of the underlying large language model provided by llama.cpp. + * @param {Object} options - Configuration options for the LLM instance. + * @param {string} options.path - The file path to the model in .gguf format. This model file contains + * the weights and configuration necessary for initializing the language model. + * @param {string} options.prompt - The initial input text to the model, setting the context or query + * for generating responses. The model uses this as a starting point for text generation. + * @param {string} [options.id] - An optional unique identifier for this specific instance of the model, + * useful for tracking or referencing the model in multi-model setups. + * @param {number} [options.n_ctx=1024] - Specifies the maximum number of tokens that the model can consider + * for a single query. This is crucial for managing memory and computational + * efficiency. Exceeding the model's configuration may lead to errors or truncated outputs. + * @param {number} [options.n_threads=8] - The number of threads allocated for the model's computation, + * affecting performance and speed of response generation. + * @param {number} [options.temp=1.1] - Sampling temperature controls the randomness of predictions. + * Higher values increase diversity, potentially at the cost of coherence. + * @param {number} [options.max_tokens=512] - The upper limit on the number of tokens that the model can generate + * in response to a single prompt. This prevents runaway generations. + * @param {number} [options.n_gpu_layers=32] - The number of GPU layers dedicated to the model processing. + * More layers can increase accuracy and complexity of the outputs. + * @param {number} [options.n_keep=0] - Determines how many of the top generated responses are retained after + * the initial generation phase. Useful for models that generate multiple outputs. + * @param {number} [options.n_batch=0] - The size of processing batches. Larger batch sizes can reduce + * the time per token generation by parallelizing computations. + * @param {number} [options.n_predict=0] - Specifies how many forward predictions the model should make + * from the current state. This can pre-generate responses or calculate probabilities. + * @param {number} [options.grp_attn_n=0] - Group attention parameter 'N' modifies how attention mechanisms + * within the model are grouped and interact, affecting the model’s focus and accuracy. + * @param {number} [options.grp_attn_w=0] - Group attention parameter 'W' adjusts the width of each attention group, + * influencing the breadth of context considered by each attention group. + * @param {number} [options.seed=0] - A seed for the random number generator used in the model. Setting this ensures + * consistent results in model outputs, important for reproducibility in experiments. + * @param {number} [options.top_k=0] - Limits the model's output choices to the top 'k' most probable next words, + * reducing the risk of less likely, potentially nonsensical outputs. + * @param {number} [options.tok_p=0.0] - Top-p (nucleus) sampling threshold, filtering the token selection pool + * to only those whose cumulative probability exceeds this value, enhancing output relevance. + * @param {number} [options.min_p=0.0] - Sets a minimum probability filter for token generation, ensuring + * that generated tokens have at least this likelihood of being relevant or coherent. + * @param {number} [options.tfs_z=0.0] - Temperature factor scale for zero-shot learning scenarios, adjusting how + * the model weights novel or unseen prompts during generation. + * @throws {Error} Throws an error if the model path is not provided, as the model cannot initialize without it. + */ + constructor(options?: { + path: string; + prompt: string; + id?: string; + n_ctx?: number; + n_threads?: number; + temp?: number; + max_tokens?: number; + n_gpu_layers?: number; + n_keep?: number; + n_batch?: number; + n_predict?: number; + grp_attn_n?: number; + grp_attn_w?: number; + seed?: number; + top_k?: number; + tok_p?: number; + min_p?: number; + tfs_z?: number; + }); + path: string; + prompt: string; + id: string | BigInt; + /** + * Tell the LLM to stop after the next token. + * @returns {Promise} A promise that resolves when the LLM stops. + */ + stop(): Promise; + /** + * Send a message to the chat. + * @param {string} message - The message to send to the chat. + * @returns {Promise} A promise that resolves with the response from the chat. + */ + chat(message: string): Promise; + } + export default exports; + import { EventEmitter } from "events"; + import * as exports from "ai"; + namespace ___home_werle_repos_socketsupply_socket_api_ai_ { } +} +declare module "window/constants" { + export const WINDOW_ERROR: -1; + export const WINDOW_NONE: 0; + export const WINDOW_CREATING: 10; + export const WINDOW_CREATED: 11; + export const WINDOW_HIDING: 20; + export const WINDOW_HIDDEN: 21; + export const WINDOW_SHOWING: 30; + export const WINDOW_SHOWN: 31; + export const WINDOW_CLOSING: 40; + export const WINDOW_CLOSED: 41; + export const WINDOW_EXITING: 50; + export const WINDOW_EXITED: 51; + export const WINDOW_KILLING: 60; + export const WINDOW_KILLED: 61; + export default exports; + import * as exports from "window/constants"; + namespace ___home_werle_repos_socketsupply_socket_api_window_constants_ { } +} +declare module "application/client" { + /** + * @typedef {{ + * id?: string | null, + * type?: 'window' | 'worker', + * parent?: object | null, + * top?: object | null, + * frameType?: 'top-level' | 'nested' | 'none' + * }} ClientState + */ + export class Client { + /** + * `Client` class constructor + * @private + * @param {ClientState} state + */ + private constructor(); + /** + * The unique ID of the client. + * @type {string|null} + */ + get id(): string; + /** + * The frame type of the client. + * @type {'top-level'|'nested'|'none'} + */ + get frameType(): "none" | "top-level" | "nested"; + /** + * The type of the client. + * @type {'window'|'worker'} + */ + get type(): "window" | "worker"; + /** + * The parent client of the client. + * @type {Client|null} + */ + get parent(): Client; + /** + * The top client of the client. + * @type {Client|null} + */ + get top(): Client; + /** + * A readonly `URL` of the current location of this client. + * @type {URL} + */ + get location(): URL; + /** + * Converts this `Client` instance to JSON. + * @return {object} + */ + toJSON(): object; + #private; + } + const _default: any; + export default _default; + export type ClientState = { + id?: string | null; + type?: "window" | "worker"; + parent?: object | null; + top?: object | null; + frameType?: "top-level" | "nested" | "none"; + }; +} +declare module "window/hotkey" { + /** + * Normalizes an expression string. + * @param {string} expression + * @return {string} + */ + export function normalizeExpression(expression: string): string; + /** + * Bind a global hotkey expression. + * @param {string} expression + * @param {{ passive?: boolean }} [options] + * @return {Promise} + */ + export function bind(expression: string, options?: { + passive?: boolean; + }): Promise; + /** + * Bind a global hotkey expression. + * @param {string} expression + * @param {object=} [options] + * @return {Promise} + */ + export function unbind(id: any, options?: object | undefined): Promise; + /** + * Get all known globally register hotkey bindings. + * @param {object=} [options] + * @return {Promise} + */ + export function getBindings(options?: object | undefined): Promise; + /** + * Get all known possible keyboard modifier and key mappings for + * expression bindings. + * @param {object=} [options] + * @return {Promise<{ keys: object, modifiers: object }>} + */ + export function getMappings(options?: object | undefined): Promise<{ + keys: object; + modifiers: object; + }>; + /** + * Adds an event listener to the global active bindings. This function is just + * proxy to `bindings.addEventListener`. + * @param {string} type + * @param {function(Event)} listener + * @param {(boolean|object)=} [optionsOrUseCapture] + */ + export function addEventListener(type: string, listener: (arg0: Event) => any, optionsOrUseCapture?: (boolean | object) | undefined): void; + /** + * Removes an event listener to the global active bindings. This function is + * just a proxy to `bindings.removeEventListener` + * @param {string} type + * @param {function(Event)} listener + * @param {(boolean|object)=} [optionsOrUseCapture] + */ + export function removeEventListener(type: string, listener: (arg0: Event) => any, optionsOrUseCapture?: (boolean | object) | undefined): void; + /** + * A high level bindings container map that dispatches events. + */ + export class Bindings extends EventTarget { + /** + * `Bindings` class constructor. + * @ignore + * @param {EventTarget} [sourceEventTarget] + */ + constructor(sourceEventTarget?: EventTarget); + /** + * Global `HotKeyEvent` event listener for `Binding` instance event dispatch. + * @ignore + * @param {import('../internal/events.js').HotKeyEvent} event + */ + onHotKey(event: import("internal/events").HotKeyEvent): boolean; + /** + * The number of `Binding` instances in the mapping. + * @type {number} + */ + get size(): number; + /** + * Setter for the level 1 'error'` event listener. + * @ignore + * @type {function(ErrorEvent)?} + */ + set onerror(onerror: (arg0: ErrorEvent) => any); + /** + * Level 1 'error'` event listener. + * @type {function(ErrorEvent)?} + */ + get onerror(): (arg0: ErrorEvent) => any; + /** + * Setter for the level 1 'hotkey'` event listener. + * @ignore + * @type {function(HotKeyEvent)?} + */ + set onhotkey(onhotkey: (arg0: hotkeyEvent) => any); + /** + * Level 1 'hotkey'` event listener. + * @type {function(hotkeyEvent)?} + */ + get onhotkey(): (arg0: hotkeyEvent) => any; + /** + * Initializes bindings from global context. + * @ignore + * @return {Promise} + */ + init(): Promise; + /** + * Get a binding by `id` + * @param {number} id + * @return {Binding} + */ + get(id: number): Binding; + /** + * Set a `binding` a by `id`. + * @param {number} id + * @param {Binding} binding + */ + set(id: number, binding: Binding): void; + /** + * Delete a binding by `id` + * @param {number} id + * @return {boolean} + */ + delete(id: number): boolean; + /** + * Returns `true` if a binding exists in the mapping, otherwise `false`. + * @return {boolean} + */ + has(id: any): boolean; + /** + * Known `Binding` values in the mapping. + * @return {{ next: function(): { value: Binding|undefined, done: boolean } }} + */ + values(): { + next: () => { + value: Binding | undefined; + done: boolean; + }; + }; + /** + * Known `Binding` keys in the mapping. + * @return {{ next: function(): { value: number|undefined, done: boolean } }} + */ + keys(): { + next: () => { + value: number | undefined; + done: boolean; + }; + }; + /** + * Known `Binding` ids in the mapping. + * @return {{ next: function(): { value: number|undefined, done: boolean } }} + */ + ids(): { + next: () => { + value: number | undefined; + done: boolean; + }; + }; + /** + * Known `Binding` ids and values in the mapping. + * @return {{ next: function(): { value: [number, Binding]|undefined, done: boolean } }} + */ + entries(): { + next: () => { + value: [number, Binding] | undefined; + done: boolean; + }; + }; + /** + * Bind a global hotkey expression. + * @param {string} expression + * @return {Promise} + */ + bind(expression: string): Promise; + /** + * Bind a global hotkey expression. + * @param {string} expression + * @return {Promise} + */ + unbind(expression: string): Promise; + /** + * Returns an array of all active bindings for the application. + * @return {Promise} + */ + active(): Promise; + /** + * Resets all active bindings in the application. + * @param {boolean=} [currentContextOnly] + * @return {Promise} + */ + reset(currentContextOnly?: boolean | undefined): Promise; + /** + * Implements the `Iterator` protocol for each currently registered + * active binding in this window context. The `AsyncIterator` protocol + * will probe for all gloally active bindings. + * @return {Iterator} + */ + [Symbol.iterator](): Iterator; + /** + * Implements the `AsyncIterator` protocol for each globally active + * binding registered to the application. This differs from the `Iterator` + * protocol as this will probe for _all_ active bindings in the entire + * application context. + * @return {AsyncGenerator} + */ + [Symbol.asyncIterator](): AsyncGenerator; + #private; + } + /** + * An `EventTarget` container for a hotkey binding. + */ + export class Binding extends EventTarget { + /** + * `Binding` class constructor. + * @ignore + * @param {object} data + */ + constructor(data: object); + /** + * `true` if the binding is valid, otherwise `false`. + * @type {boolean} + */ + get isValid(): boolean; + /** + * `true` if the binding is considered active, otherwise `false`. + * @type {boolean} + */ + get isActive(): boolean; + /** + * The global unique ID for this binding. + * @type {number?} + */ + get id(): number; + /** + * The computed hash for this binding expression. + * @type {number?} + */ + get hash(): number; + /** + * The normalized expression as a sequence of tokens. + * @type {string[]} + */ + get sequence(): string[]; + /** + * The original expression of the binding. + * @type {string?} + */ + get expression(): string; + /** + * Setter for the level 1 'hotkey'` event listener. + * @ignore + * @type {function(HotKeyEvent)?} + */ + set onhotkey(onhotkey: (arg0: hotkeyEvent) => any); + /** + * Level 1 'hotkey'` event listener. + * @type {function(hotkeyEvent)?} + */ + get onhotkey(): (arg0: hotkeyEvent) => any; + /** + * Binds this hotkey expression. + * @return {Promise} + */ + bind(): Promise; + /** + * Unbinds this hotkey expression. + * @return {Promise} + */ + unbind(): Promise; + /** + * Implements the `AsyncIterator` protocol for async 'hotkey' events + * on this binding instance. + * @return {AsyncGenerator} + */ + [Symbol.asyncIterator](): AsyncGenerator; + #private; + } + /** + * A container for all the bindings currently bound + * by this window context. + * @type {Bindings} + */ + export const bindings: Bindings; + export default bindings; + import { HotKeyEvent } from "internal/events"; +} +declare module "window" { + /** + * @param {string} url + * @return {string} + * @ignore + */ + export function formatURL(url: string): string; + /** + * @class ApplicationWindow + * Represents a window in the application + */ + export class ApplicationWindow { + static constants: typeof statuses; + static hotkey: import("window/hotkey").Bindings; + constructor({ index, ...state }: { + [x: string]: any; + index: any; + }); + /** + * The unique ID of this window. + * @type {string} + */ + get id(): string; + /** + * Get the index of the window + * @return {number} - the index of the window + */ + get index(): number; + /** + * @type {import('./window/hotkey.js').default} + */ + get hotkey(): import("window/hotkey").Bindings; + get state(): { + [x: string]: any; + }; + /** + * The broadcast channel for this window. + * @type {BroadcastChannel} + */ + get channel(): BroadcastChannel; + /** + * Get the size of the window + * @return {{ width: number, height: number }} - the size of the window + */ + get size(): { + width: number; + height: number; + }; + get location(): any; + /** + * get the position of the window + * @return {{ x: number, y: number }} - the position of the window + */ + get position(): { + x: number; + y: number; + }; + /** + * get the title of the window + * @return {string} - the title of the window + */ + get title(): string; + /** + * get the status of the window + * @return {string} - the status of the window + */ + get status(): string; + /** + * Get the size of the window + * @return {{ width: number, height: number }} - the size of the window + */ + getSize(): { + width: number; + height: number; + }; + /** + * Get the position of the window + * @return {{ x: number, y: number }} - the position of the window + */ + getPosition(): { + x: number; + y: number; + }; + /** + * Get the title of the window + * @return {string} - the title of the window + */ + getTitle(): string; + /** + * Get the status of the window + * @return {string} - the status of the window + */ + getStatus(): string; + /** + * Close the window + * @return {Promise} - the options of the window + */ + close(): Promise; + /** + * Shows the window + * @return {Promise} + */ + show(): Promise; + /** + * Hides the window + * @return {Promise} + */ + hide(): Promise; + /** + * Maximize the window + * @return {Promise} + */ + maximize(): Promise; + /** + * Minimize the window + * @return {Promise} + */ + minimize(): Promise; + /** + * Restore the window + * @return {Promise} + */ + restore(): Promise; + /** + * Sets the title of the window + * @param {string} title - the title of the window + * @return {Promise} + */ + setTitle(title: string): Promise; + /** + * Sets the size of the window + * @param {object} opts - an options object + * @param {(number|string)=} opts.width - the width of the window + * @param {(number|string)=} opts.height - the height of the window + * @return {Promise} + * @throws {Error} - if the width or height is invalid + */ + setSize(opts: { + width?: (number | string) | undefined; + height?: (number | string) | undefined; + }): Promise; + /** + * Sets the position of the window + * @param {object} opts - an options object + * @param {(number|string)=} opts.x - the x position of the window + * @param {(number|string)=} opts.y - the y position of the window + * @return {Promise} + * @throws {Error} - if the x or y is invalid + */ + setPosition(opts: { + x?: (number | string) | undefined; + y?: (number | string) | undefined; + }): Promise; + /** + * Navigate the window to a given path + * @param {object} path - file path + * @return {Promise} + */ + navigate(path: object): Promise; + /** + * Opens the Web Inspector for the window + * @return {Promise} + */ + showInspector(): Promise; + /** + * Sets the background color of the window + * @param {object} opts - an options object + * @param {number} opts.red - the red value + * @param {number} opts.green - the green value + * @param {number} opts.blue - the blue value + * @param {number} opts.alpha - the alpha value + * @return {Promise} + */ + setBackgroundColor(opts: { + red: number; + green: number; + blue: number; + alpha: number; + }): Promise; + /** + * Gets the background color of the window + * @return {Promise} + */ + getBackgroundColor(): Promise; + /** + * Opens a native context menu. + * @param {object} options - an options object + * @return {Promise} + */ + setContextMenu(options: object): Promise; + /** + * Shows a native open file dialog. + * @param {object} options - an options object + * @return {Promise} - an array of file paths + */ + showOpenFilePicker(options: object): Promise; + /** + * Shows a native save file dialog. + * @param {object} options - an options object + * @return {Promise} - an array of file paths + */ + showSaveFilePicker(options: object): Promise; + /** + * Shows a native directory dialog. + * @param {object} options - an options object + * @return {Promise} - an array of file paths + */ + showDirectoryFilePicker(options: object): Promise; + /** + * This is a high-level API that you should use instead of `ipc.request` when + * you want to send a message to another window or to the backend. + * + * @param {object} options - an options object + * @param {number=} options.window - the window to send the message to + * @param {boolean=} [options.backend = false] - whether to send the message to the backend + * @param {string} options.event - the event to send + * @param {(string|object)=} options.value - the value to send + * @returns + */ + send(options: { + window?: number | undefined; + backend?: boolean | undefined; + event: string; + value?: (string | object) | undefined; + }): Promise; + /** + * Post a message to a window + * TODO(@jwerle): research using `BroadcastChannel` instead + * @param {object} message + * @return {Promise} + */ + postMessage(message: object): Promise; + /** + * Opens an URL in the default application associated with the URL protocol, + * such as 'https:' for the default web browser. + * @param {string} value + * @returns {Promise<{ url: string }>} + */ + openExternal(value: string): Promise<{ + url: string; + }>; + /** + * Opens a file in the default file explorer. + * @param {string} value + * @returns {Promise} + */ + revealFile(value: string): Promise; + /** + * Updates wnidow state + * @param {string} title - the title of the window + * @return {Promise} + */ + update(): Promise; + /** + * Adds a listener to the window. + * @param {string} event - the event to listen to + * @param {function(*): void} cb - the callback to call + * @returns {void} + */ + addListener(event: string, cb: (arg0: any) => void): void; + /** + * Adds a listener to the window. An alias for `addListener`. + * @param {string} event - the event to listen to + * @param {function(*): void} cb - the callback to call + * @returns {void} + * @see addListener + */ + on(event: string, cb: (arg0: any) => void): void; + /** + * Adds a listener to the window. The listener is removed after the first call. + * @param {string} event - the event to listen to + * @param {function(*): void} cb - the callback to call + * @returns {void} + */ + once(event: string, cb: (arg0: any) => void): void; + /** + * Removes a listener from the window. + * @param {string} event - the event to remove the listener from + * @param {function(*): void} cb - the callback to remove + * @returns {void} + */ + removeListener(event: string, cb: (arg0: any) => void): void; + /** + * Removes all listeners from the window. + * @param {string} event - the event to remove the listeners from + * @returns {void} + */ + removeAllListeners(event: string): void; + /** + * Removes a listener from the window. An alias for `removeListener`. + * @param {string} event - the event to remove the listener from + * @param {function(*): void} cb - the callback to remove + * @returns {void} + * @see removeListener + */ + off(event: string, cb: (arg0: any) => void): void; + #private; + } + export default ApplicationWindow; + /** + * @ignore + */ + export const constants: typeof statuses; + import ipc from "ipc"; + import * as statuses from "window/constants"; + import client from "application/client"; + import hotkey from "window/hotkey"; + export { client, hotkey }; +} +declare module "application" { + /** + * Add an application event `type` callback `listener` with `options`. + * @param {string} type + * @param {function(Event|MessageEvent|CustomEvent|ApplicationURLEvent): boolean} listener + * @param {{ once?: boolean }|boolean=} [options] + */ + export function addEventListener(type: string, listener: (arg0: Event | MessageEvent | CustomEvent | ApplicationURLEvent) => boolean, options?: ({ + once?: boolean; + } | boolean) | undefined): void; + /** + * Remove an application event `type` callback `listener` with `options`. + * @param {string} type + * @param {function(Event|MessageEvent|CustomEvent|ApplicationURLEvent): boolean} listener + */ + export function removeEventListener(type: string, listener: (arg0: Event | MessageEvent | CustomEvent | ApplicationURLEvent) => boolean): void; + /** + * Returns the current window index + * @return {number} + */ + export function getCurrentWindowIndex(): number; + /** + * Creates a new window and returns an instance of ApplicationWindow. + * @param {object} opts - an options object + * @param {string=} opts.aspectRatio - a string (split on ':') provides two float values which set the window's aspect ratio. + * @param {boolean=} opts.closable - deterime if the window can be closed. + * @param {boolean=} opts.minimizable - deterime if the window can be minimized. + * @param {boolean=} opts.maximizable - deterime if the window can be maximized. + * @param {number} [opts.margin] - a margin around the webview. (Private) + * @param {number} [opts.radius] - a radius on the webview. (Private) + * @param {number} opts.index - the index of the window. + * @param {string} opts.path - the path to the HTML file to load into the window. + * @param {string=} opts.title - the title of the window. + * @param {string=} opts.titlebarStyle - determines the style of the titlebar (MacOS only). + * @param {string=} opts.windowControlOffsets - a string (split on 'x') provides the x and y position of the traffic lights (MacOS only). + * @param {string=} opts.backgroundColorDark - determines the background color of the window in dark mode. + * @param {string=} opts.backgroundColorLight - determines the background color of the window in light mode. + * @param {(number|string)=} opts.width - the width of the window. If undefined, the window will have the main window width. + * @param {(number|string)=} opts.height - the height of the window. If undefined, the window will have the main window height. + * @param {(number|string)=} [opts.minWidth = 0] - the minimum width of the window + * @param {(number|string)=} [opts.minHeight = 0] - the minimum height of the window + * @param {(number|string)=} [opts.maxWidth = '100%'] - the maximum width of the window + * @param {(number|string)=} [opts.maxHeight = '100%'] - the maximum height of the window + * @param {boolean=} [opts.resizable=true] - whether the window is resizable + * @param {boolean=} [opts.frameless=false] - whether the window is frameless + * @param {boolean=} [opts.utility=false] - whether the window is utility (macOS only) + * @param {boolean=} [opts.shouldExitApplicationOnClose=false] - whether the window can exit the app + * @param {boolean=} [opts.headless=false] - whether the window will be headless or not (no frame) + * @param {string=} [opts.userScript=null] - A user script that will be injected into the window (desktop only) + * @param {string[]=} [opts.protocolHandlers] - An array of protocol handler schemes to register with the new window (requires service worker) + * @param {string=} [opts.resourcesDirectory] + * @param {boolean=} [opts.shouldPreferServiceWorker=false] + * @return {Promise} + */ + export function createWindow(opts: { + aspectRatio?: string | undefined; + closable?: boolean | undefined; + minimizable?: boolean | undefined; + maximizable?: boolean | undefined; + margin?: number; + radius?: number; + index: number; + path: string; + title?: string | undefined; + titlebarStyle?: string | undefined; + windowControlOffsets?: string | undefined; + backgroundColorDark?: string | undefined; + backgroundColorLight?: string | undefined; + width?: (number | string) | undefined; + height?: (number | string) | undefined; + minWidth?: (number | string) | undefined; + minHeight?: (number | string) | undefined; + maxWidth?: (number | string) | undefined; + maxHeight?: (number | string) | undefined; + resizable?: boolean | undefined; + frameless?: boolean | undefined; + utility?: boolean | undefined; + shouldExitApplicationOnClose?: boolean | undefined; + headless?: boolean | undefined; + userScript?: string | undefined; + protocolHandlers?: string[] | undefined; + resourcesDirectory?: string | undefined; + shouldPreferServiceWorker?: boolean | undefined; + }): Promise; + /** + * Returns the current screen size. + * @returns {Promise<{ width: number, height: number }>} + */ + export function getScreenSize(): Promise<{ + width: number; + height: number; + }>; + /** + * Returns the ApplicationWindow instances for the given indices or all windows if no indices are provided. + * @param {number[]} [indices] - the indices of the windows + * @throws {Error} - if indices is not an array of integer numbers + * @return {Promise} + */ + export function getWindows(indices?: number[], options?: any): Promise; + /** + * Returns the ApplicationWindow instance for the given index + * @param {number} index - the index of the window + * @throws {Error} - if index is not a valid integer number + * @returns {Promise} - the ApplicationWindow instance or null if the window does not exist + */ + export function getWindow(index: number, options: any): Promise; + /** + * Returns the ApplicationWindow instance for the current window. + * @return {Promise} + */ + export function getCurrentWindow(): Promise; + /** + * Quits the backend process and then quits the render process, the exit code used is the final exit code to the OS. + * @param {number} [code = 0] - an exit code + * @return {Promise} + */ + export function exit(code?: number): Promise; + /** + * Set the native menu for the app. + * + * @param {object} options - an options object + * @param {string} options.value - the menu layout + * @param {number} options.index - the window to target (if applicable) + * @return {Promise} + * + * Socket Runtime provides a minimalist DSL that makes it easy to create + * cross platform native system and context menus. + * + * Menus are created at run time. They can be created from either the Main or + * Render process. The can be recreated instantly by calling the `setSystemMenu` method. + * + * The method takes a string. Here's an example of a menu. The semi colon is + * significant indicates the end of the menu. Use an underscore when there is no + * accelerator key. Modifiers are optional. And well known OS menu options like + * the edit menu will automatically get accelerators you dont need to specify them. + * + * + * ```js + * socket.application.setSystemMenu({ index: 0, value: ` + * App: + * Foo: f; + * + * Edit: + * Cut: x + * Copy: c + * Paste: v + * Delete: _ + * Select All: a; + * + * Other: + * Apple: _ + * Another Test: T + * !Im Disabled: I + * Some Thing: S + Meta + * --- + * Bazz: s + Meta, Control, Alt; + * `) + * ``` + * + * Separators + * + * To create a separator, use three dashes `---`. + * + * + * Accelerator Modifiers + * + * Accelerator modifiers are used as visual indicators but don't have a + * material impact as the actual key binding is done in the event listener. + * + * A capital letter implies that the accelerator is modified by the `Shift` key. + * + * Additional accelerators are `Meta`, `Control`, `Option`, each separated + * by commas. If one is not applicable for a platform, it will just be ignored. + * + * On MacOS `Meta` is the same as `Command`. + * + * + * Disabled Items + * + * If you want to disable a menu item just prefix the item with the `!` character. + * This will cause the item to appear disabled when the system menu renders. + * + * + * Submenus + * + * We feel like nested menus are an anti-pattern. We don't use them. If you have a + * strong argument for them and a very simple pull request that makes them work we + * may consider them. + * + * + * Event Handling + * + * When a menu item is activated, it raises the `menuItemSelected` event in + * the front end code, you can then communicate with your backend code if you + * want from there. + * + * For example, if the `Apple` item is selected from the `Other` menu... + * + * ```js + * window.addEventListener('menuItemSelected', event => { + * assert(event.detail.parent === 'Other') + * assert(event.detail.title === 'Apple') + * }) + * ``` + * + */ + export function setSystemMenu(o: any): Promise; + /** + * An alias to setSystemMenu for creating a tary menu + */ + export function setTrayMenu(o: any): Promise; + /** + * Set the enabled state of the system menu. + * @param {object} value - an options object + * @return {Promise} + */ + export function setSystemMenuItemEnabled(value: object): Promise; + /** + * Predicate function to determine if application is in a "paused" state. + * @return {boolean} + */ + export function isPaused(): boolean; + export const MAX_WINDOWS: 32; + export class ApplicationWindowList { + static from(...args: any[]): exports.ApplicationWindowList; + constructor(items: any); + get length(): number; + get size(): number; + forEach(callback: any, thisArg: any): void; + item(index: any): any; + entries(): any[][]; + keys(): any[]; + values(): any[]; + add(window: any): this; + remove(windowOrIndex: any): boolean; + contains(windowOrIndex: any): boolean; + clear(): this; + get [Symbol.iterator](): () => IterableIterator; + #private; + } + /** + * Socket Runtime version. + * @type {object} - an object containing the version information + */ + export const runtimeVersion: object; + /** + * Runtime debug flag. + * @type {boolean} + */ + export const debug: boolean; + /** + * Application configuration. + * @type {Record} + */ + export const config: Record; + export namespace backend { + /** + * @param {object} opts - an options object + * @param {boolean} [opts.force = false] - whether to force the existing process to close + * @return {Promise} + */ + function open(opts?: { + force?: boolean; + }): Promise; + /** + * @return {Promise} + */ + function close(): Promise; + } + export default exports; + import { ApplicationURLEvent } from "internal/events"; + import ApplicationWindow from "window"; + import ipc from "ipc"; + import client from "application/client"; + import menu from "application/menu"; + import * as exports from "application"; + namespace ___home_werle_repos_socketsupply_socket_api_application_ { } + export { client, menu }; +} +declare module "test/fast-deep-equal" { + export default function equal(a: any, b: any): boolean; +} +declare module "assert" { + export function assert(value: any, message?: any): void; + export function ok(value: any, message?: any): void; + export function equal(actual: any, expected: any, message?: any): void; + export function notEqual(actual: any, expected: any, message?: any): void; + export function strictEqual(actual: any, expected: any, message?: any): void; + export function notStrictEqual(actual: any, expected: any, message?: any): void; + export function deepEqual(actual: any, expected: any, message?: any): void; + export function notDeepEqual(actual: any, expected: any, message?: any): void; + export class AssertionError extends Error { + constructor(options: any); + actual: any; + expected: any; + operator: any; + } + const _default: typeof assert & { + AssertionError: typeof AssertionError; + ok: typeof ok; + equal: typeof equal; + notEqual: typeof notEqual; + strictEqual: typeof strictEqual; + notStrictEqual: typeof notStrictEqual; + deepEqual: typeof deepEqual; + notDeepEqual: typeof notDeepEqual; + }; + export default _default; +} +declare module "async_hooks" { + export default exports; + import { AsyncLocalStorage } from "async/storage"; + import { AsyncResource } from "async/resource"; + import { executionAsyncResource } from "async/hooks"; + import { executionAsyncId } from "async/hooks"; + import { triggerAsyncId } from "async/hooks"; + import { createHook } from "async/hooks"; + import * as exports from "async_hooks"; + namespace ___home_werle_repos_socketsupply_socket_api_async_hooks_ { } + export { AsyncLocalStorage, AsyncResource, executionAsyncResource, executionAsyncId, triggerAsyncId, createHook }; +} +declare module "bluetooth" { + export default exports; + /** + * Create an instance of a Bluetooth service. + */ + export class Bluetooth extends EventEmitter { + static isInitalized: boolean; + /** + * constructor is an example property that is set to `true` + * Creates a new service with key-value pairs + * @param {string} serviceId - Given a default value to determine the type + */ + constructor(serviceId?: string); + serviceId: string; + /** + * Start the Bluetooth service. + * @return {Promise} + * + */ + start(): Promise; + /** + * Start scanning for published values that correspond to a well-known UUID. + * Once subscribed to a UUID, events that correspond to that UUID will be + * emitted. To receive these events you can add an event listener, for example... + * + * ```js + * const ble = new Bluetooth(id) + * ble.subscribe(uuid) + * ble.on(uuid, (data, details) => { + * // ...do something interesting + * }) + * ``` + * + * @param {string} [id = ''] - A well-known UUID + * @return {Promise} + */ + subscribe(id?: string): Promise; + /** + * Start advertising a new value for a well-known UUID + * @param {string} [id=''] - A well-known UUID + * @param {string} [value=''] + * @return {Promise} + */ + publish(id?: string, value?: string): Promise; + } + import * as exports from "bluetooth"; + import { EventEmitter } from "events"; + import ipc from "ipc"; + namespace ___home_werle_repos_socketsupply_socket_api_bluetooth_ { } +} +declare module "bootstrap" { + /** + * @param {string} dest - file path + * @param {string} hash - hash string + * @param {string} hashAlgorithm - hash algorithm + * @returns {Promise} + */ + export function checkHash(dest: string, hash: string, hashAlgorithm: string): Promise; + export function bootstrap(options: any): Bootstrap; + namespace _default { + export { bootstrap }; + export { checkHash }; + } + export default _default; + class Bootstrap extends EventEmitter { + constructor(options: any); + options: any; + run(): Promise; + /** + * @param {object} options + * @param {Uint8Array} options.fileBuffer + * @param {string} options.dest + * @returns {Promise} + */ + write({ fileBuffer, dest }: { + fileBuffer: Uint8Array; + dest: string; + }): Promise; + /** + * @param {string} url - url to download + * @returns {Promise} + * @throws {Error} - if status code is not 200 + */ + download(url: string): Promise; + cleanup(): void; + } + import { EventEmitter } from "events"; +} +declare module "shared-worker/index" { + export function init(sharedWorker: any, options: any): Promise; + /** + * Gets the SharedWorker context window. + * This function will create it if it does not already exist. + * @return {Promise; + export const SHARED_WORKER_WINDOW_INDEX: 46; + export const SHARED_WORKER_WINDOW_TITLE: "socket:shared-worker"; + export const SHARED_WORKER_WINDOW_PATH: "/socket/shared-worker/index.html"; + export const channel: ipc.IPCBroadcastChannel; + export const workers: Map; + export class SharedWorkerMessagePort extends ipc.IPCMessagePort { + } + export class SharedWorker extends EventTarget { + /** + * `SharedWorker` class constructor. + * @param {string|URL|Blob} aURL + * @param {string|object=} [nameOrOptions] + */ + constructor(aURL: string | URL | Blob, nameOrOptions?: (string | object) | undefined); + set onerror(onerror: any); + get onerror(): any; + get ready(): any; + get port(): any; + get id(): any; + #private; + } + export default SharedWorker; + import ipc from "ipc"; +} +declare module "internal/promise" { + export const NativePromise: PromiseConstructor; + export namespace NativePromisePrototype { + export let then: (onfulfilled?: (value: any) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => globalThis.Promise; + let _catch: (onrejected?: (reason: any) => TResult | PromiseLike) => globalThis.Promise; + export { _catch as catch }; + let _finally: (onfinally?: () => void) => globalThis.Promise; + export { _finally as finally }; + } + export const NativePromiseAll: any; + export const NativePromiseAny: any; + /** + * @typedef {function(any): void} ResolveFunction + */ + /** + * @typedef {function(Error|string|null): void} RejectFunction + */ + /** + * @typedef {function(ResolveFunction, RejectFunction): void} ResolverFunction + */ + /** + * @typedef {{ + * promise: Promise, + * resolve: ResolveFunction, + * reject: RejectFunction + * }} PromiseResolvers + */ + export class Promise extends globalThis.Promise { + /** + * Creates a new `Promise` with resolver functions. + * @see {https://github.com/tc39/proposal-promise-with-resolvers} + * @return {PromiseResolvers} + */ + static withResolvers(): PromiseResolvers; + /** + * `Promise` class constructor. + * @ignore + * @param {ResolverFunction} resolver + */ + constructor(resolver: ResolverFunction); + [resourceSymbol]: { + "__#16@#type": any; + "__#16@#destroyed": boolean; + "__#16@#asyncId": number; + "__#16@#triggerAsyncId": any; + "__#16@#requireManualDestroy": boolean; + readonly type: string; + readonly destroyed: boolean; + asyncId(): number; + triggerAsyncId(): number; + emitDestroy(): CoreAsyncResource; + bind(fn: Function, thisArg?: object | undefined): Function; + runInAsyncScope(fn: Function, thisArg?: object | undefined, ...args?: any[]): any; + }; + } + export namespace Promise { + function all(iterable: any): any; + function any(iterable: any): any; + } + export default Promise; + export type ResolveFunction = (arg0: any) => void; + export type RejectFunction = (arg0: Error | string | null) => void; + export type ResolverFunction = (arg0: ResolveFunction, arg1: RejectFunction) => void; + export type PromiseResolvers = { + promise: Promise; + resolve: ResolveFunction; + reject: RejectFunction; + }; + const resourceSymbol: unique symbol; + import * as asyncHooks from "internal/async/hooks"; +} +declare module "internal/globals" { + /** + * Gets a runtime global value by name. + * @ignore + * @param {string} name + * @return {any|null} + */ + export function get(name: string): any | null; + /** + * Symbolic global registry + * @ignore + */ + export class GlobalsRegistry { + get global(): any; + symbol(name: any): symbol; + register(name: any, value: any): any; + get(name: any): any; + } + export default registry; + const registry: any; +} +declare module "console" { + export function patchGlobalConsole(globalConsole: any, options?: {}): any; + export const globalConsole: globalThis.Console; + export class Console { + /** + * @ignore + */ + constructor(options: any); + /** + * @type {import('dom').Console} + */ + console: any; + /** + * @type {Map} + */ + timers: Map; + /** + * @type {Map} + */ + counters: Map; + /** + * @type {function?} + */ + postMessage: Function | null; + write(destination: any, ...args: any[]): any; + assert(assertion: any, ...args: any[]): void; + clear(): void; + count(label?: string): void; + countReset(label?: string): void; + debug(...args: any[]): void; + dir(...args: any[]): void; + dirxml(...args: any[]): void; + error(...args: any[]): void; + info(...args: any[]): void; + log(...args: any[]): void; + table(...args: any[]): any; + time(label?: string): void; + timeEnd(label?: string): void; + timeLog(label?: string): void; + trace(...objects: any[]): void; + warn(...args: any[]): void; + } + const _default: Console & { + Console: typeof Console; + globalConsole: globalThis.Console; + }; + export default _default; +} +declare module "vm" { + /** + * @ignore + * @param {object[]} transfer + * @param {object} object + * @param {object=} [options] + * @return {object[]} + */ + export function findMessageTransfers(transfers: any, object: object, options?: object | undefined): object[]; + /** + * @ignore + * @param {object} context + */ + export function applyInputContextReferences(context: object): void; + /** + * @ignore + * @param {object} context + */ + export function applyOutputContextReferences(context: object): void; + /** + * @ignore + * @param {object} context + */ + export function filterNonTransferableValues(context: object): void; + /** + * @ignore + * @param {object=} [currentContext] + * @param {object=} [updatedContext] + * @param {object=} [contextReference] + * @return {{ deletions: string[], merges: string[] }} + */ + export function applyContextDifferences(currentContext?: object | undefined, updatedContext?: object | undefined, contextReference?: object | undefined, preserveScriptArgs?: boolean): { + deletions: string[]; + merges: string[]; + }; + /** + * Wrap a JavaScript function source. + * @ignore + * @param {string} source + * @param {object=} [options] + */ + export function wrapFunctionSource(source: string, options?: object | undefined): string; + /** + * Gets the VM context window. + * This function will create it if it does not already exist. + * @return {Promise; + /** + * Gets the `SharedWorker` that for the VM context. + * @return {Promise} + */ + export function getContextWorker(): Promise; + /** + * Terminates the VM script context window. + * @ignore + */ + export function terminateContextWindow(): Promise; + /** + * Terminates the VM script context worker. + * @ignore + */ + export function terminateContextWorker(): Promise; + /** + * Creates a prototype object of known global reserved intrinsics. + * @ignore + */ + export function createIntrinsics(options: any): any; + /** + * Returns `true` if value is an intrinsic, otherwise `false`. + * @param {any} value + * @return {boolean} + */ + export function isIntrinsic(value: any): boolean; + /** + * Get the intrinsic type of a given `value`. + * @param {any} + * @return {function|object|null|undefined} + */ + export function getIntrinsicType(value: any): Function | object | null | undefined; + /** + * Get the intrinsic type string of a given `value`. + * @param {any} + * @return {string|null} + */ + export function getIntrinsicTypeString(value: any): string | null; + /** + * Creates a global proxy object for context execution. + * @ignore + * @param {object} context + * @param {object=} [options] + * @return {Proxy} + */ + export function createGlobalObject(context: object, options?: object | undefined): ProxyConstructor; + /** + * @ignore + * @param {string} source + * @return {boolean} + */ + export function detectFunctionSourceType(source: string): boolean; + /** + * Compiles `source` with `options` into a function. + * @ignore + * @param {string} source + * @param {object=} [options] + * @return {function} + */ + export function compileFunction(source: string, options?: object | undefined): Function; + /** + * Run `source` JavaScript in given context. The script context execution + * context is preserved until the `context` object that points to it is + * garbage collected or there are no longer any references to it and its + * associated `Script` instance. + * @param {string|object|function} source + * @param {object=} [context] + * @param {ScriptOptions=} [options] + * @return {Promise} + */ + export function runInContext(source: string | object | Function, context?: object | undefined, options?: ScriptOptions | undefined): Promise; + /** + * Run `source` JavaScript in new context. The script context is destroyed after + * execution. This is typically a "one off" isolated run. + * @param {string} source + * @param {object=} [context] + * @param {ScriptOptions=} [options] + * @return {Promise} + */ + export function runInNewContext(source: string, context?: object | undefined, options?: ScriptOptions | undefined): Promise; + /** + * Run `source` JavaScript in this current context (`globalThis`). + * @param {string} source + * @param {ScriptOptions=} [options] + * @return {Promise} + */ + export function runInThisContext(source: string, options?: ScriptOptions | undefined): Promise; + /** + * @ignore + * @param {Reference} reference + */ + export function putReference(reference: Reference): void; + /** + * Create a `Reference` for a `value` in a script `context`. + * @param {any} value + * @param {object} context + * @param {object=} [options] + * @return {Reference} + */ + export function createReference(value: any, context: object, options?: object | undefined): Reference; + /** + * Get a script context by ID or values + * @param {string|object|function} id + * @return {Reference?} + */ + export function getReference(id: string | object | Function): Reference | null; + /** + * Remove a script context reference by ID. + * @param {string} id + */ + export function removeReference(id: string): void; + /** + * Get all transferable values in the `object` hierarchy. + * @param {object} object + * @return {object[]} + */ + export function getTransferables(object: object): object[]; + /** + * @ignore + * @param {object} object + * @return {object} + */ + export function createContext(object: object): object; + /** + * Returns `true` if `object` is a "context" object. + * @param {object} + * @return {boolean} + */ + export function isContext(object: any): boolean; + /** + * Shared broadcast for virtual machaines + * @type {BroadcastChannel} + */ + export const channel: BroadcastChannel; + /** + * A container for a context worker message channel that looks like a "worker". + * @ignore + */ + export class ContextWorkerInterface extends EventTarget { + get channel(): any; + get port(): any; + destroy(): void; + #private; + } + /** + * A container proxy for a context worker message channel that + * looks like a "worker". + * @ignore + */ + export class ContextWorkerInterfaceProxy extends EventTarget { + constructor(globals: any); + get port(): any; + #private; + } + /** + * Global reserved values that a script context may not modify. + * @type {string[]} + */ + export const RESERVED_GLOBAL_INTRINSICS: string[]; + /** + * A unique reference to a value owner by a "context object" and a + * `Script` instance. + */ + export class Reference { + /** + * Predicate function to determine if a `value` is an internal or external + * script reference value. + * @param {amy} value + * @return {boolean} + */ + static isReference(value: amy): boolean; + /** + * `Reference` class constructor. + * @param {string} id + * @param {any} value + * @param {object=} [context] + * @param {object=} [options] + */ + constructor(id: string, value: any, context?: object | undefined, options?: object | undefined); + /** + * The unique id of the reference + * @type {string} + */ + get id(): string; + /** + * The underling primitive type of the reference value. + * @ignore + * @type {'undefined'|'object'|'number'|'boolean'|'function'|'symbol'} + */ + get type(): "number" | "boolean" | "symbol" | "undefined" | "object" | "function"; + /** + * The underlying value of the reference. + * @type {any?} + */ + get value(): any; + /** + * The name of the type. + * @type {string?} + */ + get name(): string; + /** + * The `Script` this value belongs to, if available. + * @type {Script?} + */ + get script(): Script; + /** + * The "context object" this reference value belongs to. + * @type {object?} + */ + get context(): any; + /** + * A boolean value to indicate if the underlying reference value is an + * intrinsic value. + * @type {boolean} + */ + get isIntrinsic(): boolean; + /** + * A boolean value to indicate if the underlying reference value is an + * external reference value. + * @type {boolean} + */ + get isExternal(): boolean; + /** + * The intrinsic type this reference may be an instance of or directly refer to. + * @type {function|object} + */ + get intrinsicType(): any; + /** + * Releases strongly held value and weak references + * to the "context object". + */ + release(): void; + /** + * Converts this `Reference` to a JSON object. + * @param {boolean=} [includeValue = false] + */ + toJSON(includeValue?: boolean | undefined): { + __vmScriptReference__: boolean; + id: string; + type: "number" | "boolean" | "symbol" | "undefined" | "object" | "function"; + name: string; + isIntrinsic: boolean; + intrinsicType: string; + }; + #private; + } + /** + * @typedef {{ + * filename?: string, + * context?: object + * }} ScriptOptions + */ + /** + * A `Script` is a container for raw JavaScript to be executed in + * a completely isolated virtual machine context, optionally with + * user supplied context. Context objects references are not actually + * shared, but instead provided to the script execution context using the + * structured cloning algorithm used by the Message Channel API. Context + * differences are computed and applied after execution so the user supplied + * context object realizes context changes after script execution. All script + * sources run in an "async" context so a "top level await" should work. + */ + export class Script extends EventTarget { + /** + * `Script` class constructor + * @param {string} source + * @param {ScriptOptions} [options] + */ + constructor(source: string, options?: ScriptOptions); + /** + * The script identifier. + */ + get id(): any; + /** + * The source for this script. + * @type {string} + */ + get source(): string; + /** + * The filename for this script. + * @type {string} + */ + get filename(): string; + /** + * A promise that resolves when the script is ready. + * @type {Promise} + */ + get ready(): Promise; + /** + * The default script context object + * @type {object} + */ + get context(): any; + /** + * Destroy the script execution context. + * @return {Promise} + */ + destroy(): Promise; + /** + * Run `source` JavaScript in given context. The script context execution + * context is preserved until the `context` object that points to it is + * garbage collected or there are no longer any references to it and its + * associated `Script` instance. + * @param {ScriptOptions=} [options] + * @param {object=} [context] + * @return {Promise} + */ + runInContext(context?: object | undefined, options?: ScriptOptions | undefined): Promise; + /** + * Run `source` JavaScript in new context. The script context is destroyed after + * execution. This is typically a "one off" isolated run. + * @param {ScriptOptions=} [options] + * @param {object=} [context] + * @return {Promise} + */ + runInNewContext(context?: object | undefined, options?: ScriptOptions | undefined): Promise; + /** + * Run `source` JavaScript in this current context (`globalThis`). + * @param {ScriptOptions=} [options] + * @return {Promise} + */ + runInThisContext(options?: ScriptOptions | undefined): Promise; + #private; + } + namespace _default { + export { createGlobalObject }; + export { compileFunction }; + export { createReference }; + export { getContextWindow }; + export { getContextWorker }; + export { getReference }; + export { getTransferables }; + export { putReference }; + export { Reference }; + export { removeReference }; + export { runInContext }; + export { runInNewContext }; + export { runInThisContext }; + export { Script }; + export { createContext }; + export { isContext }; + export { channel }; + } + export default _default; + export type ScriptOptions = { + filename?: string; + context?: object; + }; + import { SharedWorker } from "shared-worker/index"; +} +declare module "worker_threads/init" { + export const SHARE_ENV: unique symbol; + export const isMainThread: boolean; + export namespace state { + export { isMainThread }; + export let parentPort: any; + export let mainPort: any; + export let workerData: any; + export let url: any; + export let env: {}; + export let id: number; + } + namespace _default { + export { state }; + } + export default _default; +} +declare module "worker_threads" { + /** + * Set shared worker environment data. + * @param {string} key + * @param {any} value + */ + export function setEnvironmentData(key: string, value: any): void; + /** + * Get shared worker environment data. + * @param {string} key + * @return {any} + */ + export function getEnvironmentData(key: string): any; + /** + + * A pool of known worker threads. + * @type {} + */ + export const workers: () => () => any; + /** + * `true` if this is the "main" thread, otherwise `false` + * The "main" thread is the top level webview window. + * @type {boolean} + */ + export const isMainThread: boolean; + /** + * The main thread `MessagePort` which is `null` when the + * current context is not the "main thread". + * @type {MessagePort?} + */ + export const mainPort: MessagePort | null; + /** + * A worker thread `BroadcastChannel` class. + */ + export class BroadcastChannel extends globalThis.BroadcastChannel { + } + /** + * A worker thread `MessageChannel` class. + */ + export class MessageChannel extends globalThis.MessageChannel { + } + /** + * A worker thread `MessagePort` class. + */ + export class MessagePort extends globalThis.MessagePort { + } + /** + * The current unique thread ID. + * @type {number} + */ + export const threadId: number; + /** + * The parent `MessagePort` instance + * @type {MessagePort?} + */ + export const parentPort: MessagePort | null; + /** + * Transferred "worker data" when creating a new `Worker` instance. + * @type {any?} + */ + export const workerData: any | null; + export class Pipe extends AsyncResource { + /** + * `Pipe` class constructor. + * @param {Childworker} worker + * @ignore + */ + constructor(worker: Childworker); + /** + * `true` if the pipe is still reading, otherwise `false`. + * @type {boolean} + */ + get reading(): boolean; + /** + * Destroys the pipe + */ + destroy(): void; + #private; + } + /** + * @typedef {{ + * env?: object, + * stdin?: boolean = false, + * stdout?: boolean = false, + * stderr?: boolean = false, + * workerData?: any, + * transferList?: any[], + * eval?: boolean = false + * }} WorkerOptions + + /** + * A worker thread that can communicate directly with a parent thread, + * share environment data, and process streamed data. + */ + export class Worker extends EventEmitter { + /** + * `Worker` class constructor. + * @param {string} filename + * @param {WorkerOptions=} [options] + */ + constructor(filename: string, options?: WorkerOptions | undefined); + /** + * Handles incoming worker messages. + * @ignore + * @param {MessageEvent} event + */ + onWorkerMessage(event: MessageEvent): boolean; + /** + * Handles process environment change events + * @ignore + * @param {import('./process.js').ProcessEnvironmentEvent} event + */ + onProcessEnvironmentEvent(event: import("process").ProcessEnvironmentEvent): void; + /** + * The unique ID for this `Worker` thread instace. + * @type {number} + */ + get id(): number; + get threadId(): number; + /** + * A `Writable` standard input stream if `{ stdin: true }` was set when + * creating this `Worker` instance. + * @type {import('./stream.js').Writable?} + */ + get stdin(): Writable; + /** + * A `Readable` standard output stream if `{ stdout: true }` was set when + * creating this `Worker` instance. + * @type {import('./stream.js').Readable?} + */ + get stdout(): Readable; + /** + * A `Readable` standard error stream if `{ stderr: true }` was set when + * creating this `Worker` instance. + * @type {import('./stream.js').Readable?} + */ + get stderr(): Readable; + /** + * Terminates the `Worker` instance + */ + terminate(): void; + postMessage(...args: any[]): void; + #private; + } + namespace _default { + export { Worker }; + export { isMainThread }; + export { parentPort }; + export { setEnvironmentData }; + export { getEnvironmentData }; + export { workerData }; + export { threadId }; + export { SHARE_ENV }; + } + export default _default; + /** + * /** + * A worker thread that can communicate directly with a parent thread, + * share environment data, and process streamed data. + */ + export type WorkerOptions = { + env?: object; + stdin?: boolean; + stdout?: boolean; + stderr?: boolean; + workerData?: any; + transferList?: any[]; + eval?: boolean; + }; + import { AsyncResource } from "async/resource"; + import { EventEmitter } from "events"; + import { Writable } from "stream"; + import { Readable } from "stream"; + import { SHARE_ENV } from "worker_threads/init"; + import init from "worker_threads/init"; + export { SHARE_ENV, init }; +} +declare module "child_process" { + /** + * Spawns a child process exeucting `command` with `args` + * @param {string} command + * @param {string[]|object=} [args] + * @param {object=} [options + * @return {ChildProcess} + */ + export function spawn(command: string, args?: (string[] | object) | undefined, options?: object | undefined): ChildProcess; + export function exec(command: any, options: any, callback: any): ChildProcess & { + then(resolve: any, reject: any): Promise; + catch(reject: any): Promise; + finally(next: any): Promise; + }; + export function execSync(command: any, options: any): any; + export class Pipe extends AsyncResource { + /** + * `Pipe` class constructor. + * @param {ChildProcess} process + * @ignore + */ + constructor(process: ChildProcess); + /** + * `true` if the pipe is still reading, otherwise `false`. + * @type {boolean} + */ + get reading(): boolean; + /** + * @type {import('./process')} + */ + get process(): typeof import("process"); + /** + * Destroys the pipe + */ + destroy(): void; + #private; + } + export class ChildProcess extends EventEmitter { + /** + * `ChildProcess` class constructor. + * @param {{ + * env?: object, + * stdin?: boolean, + * stdout?: boolean, + * stderr?: boolean, + * signal?: AbortSigal, + * }=} [options] + */ + constructor(options?: { + env?: object; + stdin?: boolean; + stdout?: boolean; + stderr?: boolean; + signal?: AbortSigal; + } | undefined); + /** + * @ignore + * @type {Pipe} + */ + get pipe(): Pipe; + /** + * `true` if the child process was killed with kill()`, + * otherwise `false`. + * @type {boolean} + */ + get killed(): boolean; + /** + * The process identifier for the child process. This value is + * `> 0` if the process was spawned successfully, otherwise `0`. + * @type {number} + */ + get pid(): number; + /** + * The executable file name of the child process that is launched. This + * value is `null` until the child process has successfully been spawned. + * @type {string?} + */ + get spawnfile(): string; + /** + * The full list of command-line arguments the child process was spawned with. + * This value is an empty array until the child process has successfully been + * spawned. + * @type {string[]} + */ + get spawnargs(): string[]; + /** + * Always `false` as the IPC messaging is not supported. + * @type {boolean} + */ + get connected(): boolean; + /** + * The child process exit code. This value is `null` if the child process + * is still running, otherwise it is a positive integer. + * @type {number?} + */ + get exitCode(): number; + /** + * If available, the underlying `stdin` writable stream for + * the child process. + * @type {import('./stream').Writable?} + */ + get stdin(): import("stream").Writable; + /** + * If available, the underlying `stdout` readable stream for + * the child process. + * @type {import('./stream').Readable?} + */ + get stdout(): import("stream").Readable; + /** + * If available, the underlying `stderr` readable stream for + * the child process. + * @type {import('./stream').Readable?} + */ + get stderr(): import("stream").Readable; + /** + * The underlying worker thread. + * @ignore + * @type {import('./worker_threads').Worker} + */ + get worker(): Worker; + /** + * This function does nothing, but is present for nodejs compat. + */ + disconnect(): boolean; + /** + * This function does nothing, but is present for nodejs compat. + * @return {boolean} + */ + send(): boolean; + /** + * This function does nothing, but is present for nodejs compat. + */ + ref(): boolean; + /** + * This function does nothing, but is present for nodejs compat. + */ + unref(): boolean; + /** + * Kills the child process. This function throws an error if the child + * process has not been spawned or is already killed. + * @param {number|string} signal + */ + kill(...args: any[]): this; + /** + * Spawns the child process. This function will thrown an error if the process + * is already spawned. + * @param {string} command + * @param {string[]=} [args] + * @return {ChildProcess} + */ + spawn(...args?: string[] | undefined): ChildProcess; + /** + * `EventTarget` based `addEventListener` method. + * @param {string} event + * @param {function(Event)} callback + * @param {{ once?: false }} [options] + */ + addEventListener(event: string, callback: (arg0: Event) => any, options?: { + once?: false; + }): void; + /** + * `EventTarget` based `removeEventListener` method. + * @param {string} event + * @param {function(Event)} callback + * @param {{ once?: false }} [options] + */ + removeEventListener(event: string, callback: (arg0: Event) => any): void; + #private; + } + export function execFile(command: any, options: any, callback: any): ChildProcess & { + then(resolve: any, reject: any): Promise; + catch(reject: any): Promise; + finally(next: any): Promise; + }; + namespace _default { + export { ChildProcess }; + export { spawn }; + export { execFile }; + export { exec }; + } + export default _default; + import { AsyncResource } from "async/resource"; + import { EventEmitter } from "events"; + import { Worker } from "worker_threads"; +} +declare module "constants" { + export * from "fs/constants"; + export * from "window/constants"; + export const E2BIG: any; + export const EACCES: any; + export const EADDRINUSE: any; + export const EADDRNOTAVAIL: any; + export const EAFNOSUPPORT: any; + export const EAGAIN: any; + export const EALREADY: any; + export const EBADF: any; + export const EBADMSG: any; + export const EBUSY: any; + export const ECANCELED: any; + export const ECHILD: any; + export const ECONNABORTED: any; + export const ECONNREFUSED: any; + export const ECONNRESET: any; + export const EDEADLK: any; + export const EDESTADDRREQ: any; + export const EDOM: any; + export const EDQUOT: any; + export const EEXIST: any; + export const EFAULT: any; + export const EFBIG: any; + export const EHOSTUNREACH: any; + export const EIDRM: any; + export const EILSEQ: any; + export const EINPROGRESS: any; + export const EINTR: any; + export const EINVAL: any; + export const EIO: any; + export const EISCONN: any; + export const EISDIR: any; + export const ELOOP: any; + export const EMFILE: any; + export const EMLINK: any; + export const EMSGSIZE: any; + export const EMULTIHOP: any; + export const ENAMETOOLONG: any; + export const ENETDOWN: any; + export const ENETRESET: any; + export const ENETUNREACH: any; + export const ENFILE: any; + export const ENOBUFS: any; + export const ENODATA: any; + export const ENODEV: any; + export const ENOENT: any; + export const ENOEXEC: any; + export const ENOLCK: any; + export const ENOLINK: any; + export const ENOMEM: any; + export const ENOMSG: any; + export const ENOPROTOOPT: any; + export const ENOSPC: any; + export const ENOSR: any; + export const ENOSTR: any; + export const ENOSYS: any; + export const ENOTCONN: any; + export const ENOTDIR: any; + export const ENOTEMPTY: any; + export const ENOTSOCK: any; + export const ENOTSUP: any; + export const ENOTTY: any; + export const ENXIO: any; + export const EOPNOTSUPP: any; + export const EOVERFLOW: any; + export const EPERM: any; + export const EPIPE: any; + export const EPROTO: any; + export const EPROTONOSUPPORT: any; + export const EPROTOTYPE: any; + export const ERANGE: any; + export const EROFS: any; + export const ESPIPE: any; + export const ESRCH: any; + export const ESTALE: any; + export const ETIME: any; + export const ETIMEDOUT: any; + export const ETXTBSY: any; + export const EWOULDBLOCK: any; + export const EXDEV: any; + export const SIGHUP: any; + export const SIGINT: any; + export const SIGQUIT: any; + export const SIGILL: any; + export const SIGTRAP: any; + export const SIGABRT: any; + export const SIGIOT: any; + export const SIGBUS: any; + export const SIGFPE: any; + export const SIGKILL: any; + export const SIGUSR1: any; + export const SIGSEGV: any; + export const SIGUSR2: any; + export const SIGPIPE: any; + export const SIGALRM: any; + export const SIGTERM: any; + export const SIGCHLD: any; + export const SIGCONT: any; + export const SIGSTOP: any; + export const SIGTSTP: any; + export const SIGTTIN: any; + export const SIGTTOU: any; + export const SIGURG: any; + export const SIGXCPU: any; + export const SIGXFSZ: any; + export const SIGVTALRM: any; + export const SIGPROF: any; + export const SIGWINCH: any; + export const SIGIO: any; + export const SIGINFO: any; + export const SIGSYS: any; + const _default: any; + export default _default; +} +declare module "timers/platform" { + export namespace platform { + let setTimeout: any; + let setInterval: any; + let setImmediate: any; + let clearTimeout: any; + let clearInterval: any; + let clearImmediate: any; + let postTask: any; + } + export default platform; +} +declare module "timers/timer" { + export class Timer extends AsyncResource { + static from(...args: any[]): Timer; + constructor(type: any, create: any, destroy: any); + get id(): number; + init(...args: any[]): this; + close(): boolean; + [Symbol.toPrimitive](): number; + #private; + } + export class Timeout extends Timer { + constructor(); + } + export class Interval extends Timer { + constructor(); + } + export class Immediate extends Timer { + constructor(); + } + namespace _default { + export { Timer }; + export { Immediate }; + export { Timeout }; + export { Interval }; + } + export default _default; + import { AsyncResource } from "async/resource"; +} +declare module "timers/promises" { + export function setTimeout(delay?: number, value?: any, options?: any): Promise; + export function setInterval(delay?: number, value?: any, options?: any): AsyncGenerator; + export function setImmediate(value?: any, options?: any): Promise; + namespace _default { + export { setImmediate }; + export { setInterval }; + export { setTimeout }; + } + export default _default; +} +declare module "timers/scheduler" { + export function wait(delay: any, options?: any): Promise; + export function postTask(callback: any, options?: any): Promise; + namespace _default { + export { postTask }; + export { setImmediate as yield }; + export { wait }; + } + export default _default; + import { setImmediate } from "timers/promises"; +} +declare module "timers/index" { + export function setTimeout(callback: any, delay: any, ...args: any[]): import("timers/timer").Timer; + export function clearTimeout(timeout: any): void; + export function setInterval(callback: any, delay: any, ...args: any[]): import("timers/timer").Timer; + export function clearInterval(interval: any): void; + export function setImmediate(callback: any, ...args: any[]): import("timers/timer").Timer; + export function clearImmediate(immediate: any): void; + /** + * Pause async execution for `timeout` milliseconds. + * @param {number} timeout + * @return {Promise} + */ + export function sleep(timeout: number): Promise; + export namespace sleep { + /** + * Pause sync execution for `timeout` milliseconds. + * @param {number} timeout + */ + function sync(timeout: number): void; + } + export { platform }; + namespace _default { + export { platform }; + export { promises }; + export { scheduler }; + export { setTimeout }; + export { clearTimeout }; + export { setInterval }; + export { clearInterval }; + export { setImmediate }; + export { clearImmediate }; + } + export default _default; + import platform from "timers/platform"; + import promises from "timers/promises"; + import scheduler from "timers/scheduler"; +} +declare module "timers" { + export * from "timers/index"; + export default exports; + import * as exports from "timers/index"; +} +declare module "internal/conduit" { + /** + * @typedef {{ options: object, payload: Uint8Array }} ReceiveMessage + * @typedef {function(Error?, ReceiveCallback | undefined)} ReceiveCallback + * @typedef {{ isActive: boolean, handles: { ids: string[], count: number }}} ConduitDiagnostics + * @typedef {{ isActive: boolean, port: number, sharedKey: string }} ConduitStatus + * @typedef {{ + * id?: string|BigInt|number, + * sharedKey?: string + *}} ConduitOptions + */ + export const DEFALUT_MAX_RECONNECT_RETRIES: 32; + export const DEFAULT_MAX_RECONNECT_TIMEOUT: 256; + /** + * A pool of known `Conduit` instances. + * @type {Set} + */ + export const pool: Set; + /** + * A container for managing a WebSocket connection to the internal runtime + * Conduit WebSocket server. + */ + export class Conduit extends EventTarget { + static set port(port: number); + /** + * The global `Conduit` port + * @type {number} + */ + static get port(): number; + /** + * Returns diagnostics information about the conduit server + * @return {Promise} + */ + static diagnostics(): Promise; + /** + * Returns the current Conduit server status + * @return {Promise} + */ + static status(): Promise; + /** + * Waits for conduit to be active + * @param {{ maxQueriesForStatus?: number }=} [options] + * @return {Promise} + */ + static waitForActiveState(options?: { + maxQueriesForStatus?: number; + } | undefined): Promise; + /** + * Gets the current conduit shared key. + * @return {Promise} + */ + static getSharedKey(): Promise; + /** + * Sets the conduit shared key. + * @param {string} sharedKey + * @return {Promise} + */ + static setSharedKey(sharedKey: string): Promise; + /** + * Creates an instance of Conduit. + * + * @param {ConduitOptions} options + */ + constructor(options: ConduitOptions); + /** + * @type {boolean} + */ + shouldReconnect: boolean; + /** + * @type {boolean} + */ + isConnecting: boolean; + /** + * @type {boolean} + */ + isActive: boolean; + /** + * @type {WebSocket?} + */ + socket: WebSocket | null; + /** + * @type {number} + */ + port: number; + /** + * @type {string} + */ + id: string; + /** + * @type {string} + */ + sharedKey: string; + /** + * The URL string for the WebSocket server. + * @type {string} + */ + get url(): string; + set onmessage(onmessage: (arg0: MessageEvent) => any); + /** + * @type {function(MessageEvent)} + */ + get onmessage(): (arg0: MessageEvent) => any; + set onerror(onerror: (arg0: ErrorEvent) => any); + /** + * @type {function(ErrorEvent)} + */ + get onerror(): (arg0: ErrorEvent) => any; + set onclose(onclose: (arg0: CloseEvent) => any); + /** + * @type {function(CloseEvent)} + */ + get onclose(): (arg0: CloseEvent) => any; + set onopen(onopen: (arg0: Event) => any); + /** + * @type {function(Event)} + */ + get onopen(): (arg0: Event) => any; + /** + * Connects the underlying conduit `WebSocket`. + * @param {function(Error?)=} [callback] + * @return {Promise} + */ + connect(callback?: ((arg0: Error | null) => any) | undefined): Promise; + /** + * Reconnects a `Conduit` socket. + * @param {{retries?: number, timeout?: number}} [options] + * @return {Promise} + */ + reconnect(options?: { + retries?: number; + timeout?: number; + }): Promise; + /** + * Encodes a single header into a Uint8Array. + * + * @private + * @param {string} key - The header key. + * @param {string} value - The header value. + * @returns {Uint8Array} The encoded header. + */ + private encodeOption; + /** + * Encodes options and payload into a single Uint8Array message. + * + * @private + * @param {object} options - The options to encode. + * @param {Uint8Array} payload - The payload to encode. + * @returns {Uint8Array} The encoded message. + */ + private encodeMessage; + /** + * Decodes a Uint8Array message into options and payload. + * @param {Uint8Array} data - The data to decode. + * @return {ReceiveMessage} The decoded message containing options and payload. + * @throws Will throw an error if the data is invalid. + */ + decodeMessage(data: Uint8Array): ReceiveMessage; + /** + * Registers a callback to handle incoming messages. + * The callback will receive an error object and an object containing + * decoded options and payload. + * @param {ReceiveCallback} callback - The callback function to handle incoming messages. + */ + receive(callback: ReceiveCallback): void; + /** + * Sends a message with the specified options and payload over the + * WebSocket connection. + * @param {object} options - The options to send. + * @param {Uint8Array=} [payload] - The payload to send. + * @return {boolean} + */ + send(options: object, payload?: Uint8Array | undefined): boolean; + /** + * Closes the WebSocket connection, preventing reconnects. + */ + close(): void; + #private; + } + export type ReceiveMessage = { + options: object; + payload: Uint8Array; + }; + export type ReceiveCallback = (arg0: Error | null, arg1: ReceiveCallback | undefined) => any; + export type ConduitDiagnostics = { + isActive: boolean; + handles: { + ids: string[]; + count: number; + }; + }; + export type ConduitStatus = { + isActive: boolean; + port: number; + sharedKey: string; + }; + export type ConduitOptions = { + id?: string | BigInt | number; + sharedKey?: string; + }; +} +declare module "ip" { + /** + * Normalizes input as an IPv4 address string + * @param {string|object|string[]|Uint8Array} input + * @return {string} + */ + export function normalizeIPv4(input: string | object | string[] | Uint8Array): string; + /** + * Determines if an input `string` is in IP address version 4 format. + * @param {string|object|string[]|Uint8Array} input + * @return {boolean} + */ + export function isIPv4(input: string | object | string[] | Uint8Array): boolean; + namespace _default { + export { normalizeIPv4 }; + export { isIPv4 }; + } + export default _default; +} +declare module "dns/promises" { + /** + * @async + * @see {@link https://nodejs.org/api/dns.html#dnspromiseslookuphostname-options} + * @param {string} hostname - The host name to resolve. + * @param {Object=} opts - An options object. + * @param {(number|string)=} [opts.family=0] - The record family. Must be 4, 6, or 0. For backward compatibility reasons,'IPv4' and 'IPv6' are interpreted as 4 and 6 respectively. The value 0 indicates that IPv4 and IPv6 addresses are both returned. Default: 0. + * @returns {Promise} + */ + export function lookup(hostname: string, opts?: any | undefined): Promise; + export default exports; + import * as exports from "dns/promises"; + namespace ___home_werle_repos_socketsupply_socket_api_dns_promises_ { } +} +declare module "dns/index" { + /** + * Resolves a host name (e.g. `example.org`) into the first found A (IPv4) or + * AAAA (IPv6) record. All option properties are optional. If options is an + * integer, then it must be 4 or 6 – if options is 0 or not provided, then IPv4 + * and IPv6 addresses are both returned if found. + * + * From the node.js website... + * + * > With the all option set to true, the arguments for callback change to (err, + * addresses), with addresses being an array of objects with the properties + * address and family. + * + * > On error, err is an Error object, where err.code is the error code. Keep in + * mind that err.code will be set to 'ENOTFOUND' not only when the host name does + * not exist but also when the lookup fails in other ways such as no available + * file descriptors. dns.lookup() does not necessarily have anything to do with + * the DNS protocol. The implementation uses an operating system facility that + * can associate names with addresses and vice versa. This implementation can + * have subtle but important consequences on the behavior of any Node.js program. + * Please take some time to consult the Implementation considerations section + * before using dns.lookup(). + * + * @see {@link https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback} + * @param {string} hostname - The host name to resolve. + * @param {(object|intenumberger)=} [options] - An options object or record family. + * @param {(number|string)=} [options.family=0] - The record family. Must be 4, 6, or 0. For backward compatibility reasons,'IPv4' and 'IPv6' are interpreted as 4 and 6 respectively. The value 0 indicates that IPv4 and IPv6 addresses are both returned. Default: 0. + * @param {function} cb - The function to call after the method is complete. + * @returns {void} + */ + export function lookup(hostname: string, options?: (object | intenumberger) | undefined, cb: Function): void; + export { promises }; + export default exports; + import * as promises from "dns/promises"; + import * as exports from "dns/index"; + namespace ___home_werle_repos_socketsupply_socket_api_dns_index_ { } +} +declare module "dns" { + export * from "dns/index"; + export default exports; + import * as exports from "dns/index"; +} +declare module "dgram" { + export function createSocket(options: string | any, callback?: Function | undefined): Socket; + /** + * New instances of dgram.Socket are created using dgram.createSocket(). + * The new keyword is not to be used to create dgram.Socket instances. + */ + export class Socket extends EventEmitter { + constructor(options: any, callback: any); + id: any; + knownIdWasGivenInSocketConstruction: boolean; + type: any; + signal: any; + state: { + recvBufferSize: any; + sendBufferSize: any; + bindState: number; + connectState: number; + reuseAddr: boolean; + ipv6Only: boolean; + }; + /** + * Listen for datagram messages on a named port and optional address + * If the address is not specified, the operating system will attempt to + * listen on all addresses. Once the binding is complete, a 'listening' + * event is emitted and the optional callback function is called. + * + * If binding fails, an 'error' event is emitted. + * + * @param {number} port - The port to listen for messages on + * @param {string} address - The address to bind to (0.0.0.0) + * @param {function} callback - With no parameters. Called when binding is complete. + * @see {@link https://nodejs.org/api/dgram.html#socketbindport-address-callback} + */ + bind(arg1: any, arg2: any, arg3: any): this; + dataListener: ({ detail }: { + detail: any; + }) => any; + conduit: Conduit; + /** + * Associates the dgram.Socket to a remote address and port. Every message sent + * by this handle is automatically sent to that destination. Also, the socket + * will only receive messages from that remote peer. Trying to call connect() + * on an already connected socket will result in an ERR_SOCKET_DGRAM_IS_CONNECTED + * exception. If the address is not provided, '0.0.0.0' (for udp4 sockets) or '::1' + * (for udp6 sockets) will be used by default. Once the connection is complete, + * a 'connect' event is emitted and the optional callback function is called. + * In case of failure, the callback is called or, failing this, an 'error' event + * is emitted. + * + * @param {number} port - Port the client should connect to. + * @param {string=} host - Host the client should connect to. + * @param {function=} connectListener - Common parameter of socket.connect() methods. Will be added as a listener for the 'connect' event once. + * @see {@link https://nodejs.org/api/dgram.html#socketconnectport-address-callback} + */ + connect(arg1: any, arg2: any, arg3: any): void; + /** + * A synchronous function that disassociates a connected dgram.Socket from + * its remote address. Trying to call disconnect() on an unbound or already + * disconnected socket will result in an ERR_SOCKET_DGRAM_NOT_CONNECTED exception. + * + * @see {@link https://nodejs.org/api/dgram.html#socketdisconnect} + */ + disconnect(): void; + /** + * Broadcasts a datagram on the socket. For connectionless sockets, the + * destination port and address must be specified. Connected sockets, on the + * other hand, will use their associated remote endpoint, so the port and + * address arguments must not be set. + * + * > The msg argument contains the message to be sent. Depending on its type, + * different behavior can apply. If msg is a Buffer, any TypedArray, or a + * DataView, the offset and length specify the offset within the Buffer where + * the message begins and the number of bytes in the message, respectively. + * If msg is a String, then it is automatically converted to a Buffer with + * 'utf8' encoding. With messages that contain multi-byte characters, offset, + * and length will be calculated with respect to byte length and not the + * character position. If msg is an array, offset and length must not be + * specified. + * + * > The address argument is a string. If the value of the address is a hostname, + * DNS will be used to resolve the address of the host. If the address is not + * provided or otherwise nullish, '0.0.0.0' (for udp4 sockets) or '::1' + * (for udp6 sockets) will be used by default. + * + * > If the socket has not been previously bound with a call to bind, the socket + * is assigned a random port number and is bound to the "all interfaces" + * address ('0.0.0.0' for udp4 sockets, '::1' for udp6 sockets.) + * + * > An optional callback function may be specified as a way of reporting DNS + * errors or for determining when it is safe to reuse the buf object. DNS + * lookups delay the time to send for at least one tick of the Node.js event + * loop. + * + * > The only way to know for sure that the datagram has been sent is by using a + * callback. If an error occurs and a callback is given, the error will be + * passed as the first argument to the callback. If a callback is not given, + * the error is emitted as an 'error' event on the socket object. + * + * > Offset and length are optional but both must be set if either is used. + * They are supported only when the first argument is a Buffer, a TypedArray, + * or a DataView. + * + * @param {Buffer | TypedArray | DataView | string | Array} msg - Message to be sent. + * @param {integer=} offset - Offset in the buffer where the message starts. + * @param {integer=} length - Number of bytes in the message. + * @param {integer=} port - Destination port. + * @param {string=} address - Destination host name or IP address. + * @param {Function=} callback - Called when the message has been sent. + * @see {@link https://nodejs.org/api/dgram.html#socketsendmsg-offset-length-port-address-callback} + */ + send(buffer: any, ...args: any[]): Promise; + /** + * Close the underlying socket and stop listening for data on it. If a + * callback is provided, it is added as a listener for the 'close' event. + * + * @param {function=} callback - Called when the connection is completed or on error. + * + * @see {@link https://nodejs.org/api/dgram.html#socketclosecallback} + */ + close(cb: any): this; + /** + * + * Returns an object containing the address information for a socket. For + * UDP sockets, this object will contain address, family, and port properties. + * + * This method throws EBADF if called on an unbound socket. + * @returns {Object} socketInfo - Information about the local socket + * @returns {string} socketInfo.address - The IP address of the socket + * @returns {string} socketInfo.port - The port of the socket + * @returns {string} socketInfo.family - The IP family of the socket + * + * @see {@link https://nodejs.org/api/dgram.html#socketaddress} + */ + address(): any; + /** + * Returns an object containing the address, family, and port of the remote + * endpoint. This method throws an ERR_SOCKET_DGRAM_NOT_CONNECTED exception + * if the socket is not connected. + * + * @returns {Object} socketInfo - Information about the remote socket + * @returns {string} socketInfo.address - The IP address of the socket + * @returns {string} socketInfo.port - The port of the socket + * @returns {string} socketInfo.family - The IP family of the socket + * @see {@link https://nodejs.org/api/dgram.html#socketremoteaddress} + */ + remoteAddress(): any; + /** + * Sets the SO_RCVBUF socket option. Sets the maximum socket receive buffer in + * bytes. + * + * @param {number} size - The size of the new receive buffer + * @see {@link https://nodejs.org/api/dgram.html#socketsetrecvbuffersizesize} + */ + setRecvBufferSize(size: number): Promise; + /** + * Sets the SO_SNDBUF socket option. Sets the maximum socket send buffer in + * bytes. + * + * @param {number} size - The size of the new send buffer + * @see {@link https://nodejs.org/api/dgram.html#socketsetsendbuffersizesize} + */ + setSendBufferSize(size: number): Promise; + /** + * @see {@link https://nodejs.org/api/dgram.html#socketgetrecvbuffersize} + */ + getRecvBufferSize(): any; + /** + * @returns {number} the SO_SNDBUF socket send buffer size in bytes. + * @see {@link https://nodejs.org/api/dgram.html#socketgetsendbuffersize} + */ + getSendBufferSize(): number; + setBroadcast(): void; + setTTL(): void; + setMulticastTTL(): void; + setMulticastLoopback(): void; + setMulticastMembership(): void; + setMulticastInterface(): void; + addMembership(): void; + dropMembership(): void; + addSourceSpecificMembership(): void; + dropSourceSpecificMembership(): void; + ref(): this; + unref(): this; + #private; + } + /** + * Generic error class for an error occurring on a `Socket` instance. + * @ignore + */ + export class SocketError extends InternalError { + /** + * @type {string} + */ + get code(): string; + } + /** + * Thrown when a socket is already bound. + */ + export class ERR_SOCKET_ALREADY_BOUND extends exports.SocketError { + get message(): string; + } + /** + * @ignore + */ + export class ERR_SOCKET_BAD_BUFFER_SIZE extends exports.SocketError { + } + /** + * @ignore + */ + export class ERR_SOCKET_BUFFER_SIZE extends exports.SocketError { + } + /** + * Thrown when the socket is already connected. + */ + export class ERR_SOCKET_DGRAM_IS_CONNECTED extends exports.SocketError { + get message(): string; + } + /** + * Thrown when the socket is not connected. + */ + export class ERR_SOCKET_DGRAM_NOT_CONNECTED extends exports.SocketError { + syscall: string; + get message(): string; + } + /** + * Thrown when the socket is not running (not bound or connected). + */ + export class ERR_SOCKET_DGRAM_NOT_RUNNING extends exports.SocketError { + get message(): string; + } + /** + * Thrown when a bad socket type is used in an argument. + */ + export class ERR_SOCKET_BAD_TYPE extends TypeError { + code: string; + get message(): string; + } + /** + * Thrown when a bad port is given. + */ + export class ERR_SOCKET_BAD_PORT extends RangeError { + code: string; + } + export default exports; + export type SocketOptions = any; + import { EventEmitter } from "events"; + import { Conduit } from "internal/conduit"; + import { InternalError } from "errors"; + import * as exports from "dgram"; + namespace ___home_werle_repos_socketsupply_socket_api_dgram_ { } +} +declare module "mime/index" { + /** + * Look up a MIME type in various MIME databases. + * @param {string} query + * @return {Promise} + */ + export function lookup(query: string): Promise; + /** + * Look up a MIME type in various MIME databases synchronously. + * @param {string} query + * @return {DatabaseQueryResult[]} + */ + export function lookupSync(query: string): DatabaseQueryResult[]; + /** + * A container for a database lookup query. + */ + export class DatabaseQueryResult { + /** + * `DatabaseQueryResult` class constructor. + * @ignore + * @param {Database|null} database + * @param {string} name + * @param {string} mime + */ + constructor(database: Database | null, name: string, mime: string); + /** + * @type {string} + */ + name: string; + /** + * @type {string} + */ + mime: string; + /** + * @type {Database?} + */ + database: Database | null; + } + /** + * A container for MIME types by class (audio, video, text, etc) + * @see {@link https://www.iana.org/assignments/media-types/media-types.xhtml} + */ + export class Database { + /** + * `Database` class constructor. + * @param {string} name + */ + constructor(name: string); + /** + * The name of the MIME database. + * @type {string} + */ + name: string; + /** + * The URL of the MIME database. + * @type {URL} + */ + url: URL; + /** + * The mapping of MIME name to the MIME "content type" + * @type {Map} + */ + map: Map; + /** + * An index of MIME "content type" to the MIME name. + * @type {Map} + */ + index: Map; + /** + * An enumeration of all database entries. + * @return {Array>} + */ + entries(): Array>; + /** + * Loads database MIME entries into internal map. + * @return {Promise} + */ + load(): Promise; + /** + * Loads database MIME entries synchronously into internal map. + */ + loadSync(): void; + /** + * Lookup MIME type by name or content type + * @param {string} query + * @return {Promise} + */ + lookup(query: string): Promise; + /** + * Lookup MIME type by name or content type synchronously. + * @param {string} query + * @return {Promise} + */ + lookupSync(query: string): Promise; + /** + * Queries database map and returns an array of results + * @param {string} query + * @return {DatabaseQueryResult[]} + */ + query(query: string): DatabaseQueryResult[]; + } + /** + * A database of MIME types for 'application/' content types + * @type {Database} + * @see {@link https://www.iana.org/assignments/media-types/media-types.xhtml#application} + */ + export const application: Database; + /** + * A database of MIME types for 'audio/' content types + * @type {Database} + * @see {@link https://www.iana.org/assignments/media-types/media-types.xhtml#audio} + */ + export const audio: Database; + /** + * A database of MIME types for 'font/' content types + * @type {Database} + * @see {@link https://www.iana.org/assignments/media-types/media-types.xhtml#font} + */ + export const font: Database; + /** + * A database of MIME types for 'image/' content types + * @type {Database} + * @see {@link https://www.iana.org/assignments/media-types/media-types.xhtml#image} + */ + export const image: Database; + /** + * A database of MIME types for 'model/' content types + * @type {Database} + * @see {@link https://www.iana.org/assignments/media-types/media-types.xhtml#model} + */ + export const model: Database; + /** + * A database of MIME types for 'multipart/' content types + * @type {Database} + * @see {@link https://www.iana.org/assignments/media-types/media-types.xhtml#multipart} + */ + export const multipart: Database; + /** + * A database of MIME types for 'text/' content types + * @type {Database} + * @see {@link https://www.iana.org/assignments/media-types/media-types.xhtml#text} + */ + export const text: Database; + /** + * A database of MIME types for 'video/' content types + * @type {Database} + * @see {@link https://www.iana.org/assignments/media-types/media-types.xhtml#video} + */ + export const video: Database; + /** + * An array of known MIME databases. Custom databases can be added to this + * array in userspace for lookup with `mime.lookup()` + * @type {Database[]} + */ + export const databases: Database[]; + namespace _default { + export { Database }; + export { databases }; + export { lookup }; + export { lookupSync }; + export { MIMEParams }; + export { MIMEType }; + export { application }; + export { audio }; + export { font }; + export { image }; + export { model }; + export { multipart }; + export { text }; + export { video }; + } + export default _default; + import { MIMEParams } from "mime/params"; + import { MIMEType } from "mime/type"; +} +declare module "mime" { + export * from "mime/index"; + export default exports; + import * as exports from "mime/index"; +} +declare module "fs/web" { + /** + * Creates a new `File` instance from `filename`. + * @param {string} filename + * @param {{ fd: fs.FileHandle, highWaterMark?: number }=} [options] + * @return {File} + */ + export function createFile(filename: string, options?: { + fd: fs.FileHandle; + highWaterMark?: number; + } | undefined): File; + /** + * Creates a `FileSystemWritableFileStream` instance backed + * by `socket:fs:` module from a given `FileSystemFileHandle` instance. + * @param {string|File} file + * @return {Promise} + */ + export function createFileSystemWritableFileStream(handle: any, options: any): Promise; + /** + * Creates a `FileSystemFileHandle` instance backed by `socket:fs:` module from + * a given `File` instance or filename string. + * @param {string|File} file + * @param {object} [options] + * @return {Promise} + */ + export function createFileSystemFileHandle(file: string | File, options?: object): Promise; + /** + * Creates a `FileSystemDirectoryHandle` instance backed by `socket:fs:` module + * from a given directory name string. + * @param {string} dirname + * @return {Promise} + */ + export function createFileSystemDirectoryHandle(dirname: string, options?: any): Promise; + export const kFileSystemHandleFullName: unique symbol; + export const kFileDescriptor: unique symbol; + export const kFileFullName: unique symbol; + export const File: { + new (fileBits: BlobPart[], fileName: string, options?: FilePropertyBag): File; + prototype: File; + } | { + new (): { + readonly lastModifiedDate: Date; + readonly lastModified: number; + readonly name: any; + readonly size: number; + readonly type: string; + slice(): void; + arrayBuffer(): Promise; + bytes(): Promise; + text(): Promise; + stream(): void; + }; + }; + export const FileSystemHandle: { + new (): { + readonly name: any; + readonly kind: any; + }; + }; + export const FileSystemFileHandle: { + new (): FileSystemFileHandle; + prototype: FileSystemFileHandle; + } | { + new (): { + getFile(): Promise; + createWritable(options?: any): Promise; + createSyncAccessHandle(): Promise; + readonly name: any; + readonly kind: any; + }; + }; + export const FileSystemDirectoryHandle: { + new (): FileSystemDirectoryHandle; + prototype: FileSystemDirectoryHandle; + } | { + new (): { + entries(): AsyncGenerator; + values(): AsyncGenerator; + keys(): AsyncGenerator; + resolve(possibleDescendant: any): Promise; + removeEntry(name: any, options?: any): Promise; + getDirectoryHandle(name: any, options?: any): Promise; + getFileHandle(name: any, options?: any): Promise; + readonly name: any; + readonly kind: any; + }; + }; + export const FileSystemWritableFileStream: { + new (underlyingSink?: UnderlyingSink, strategy?: QueuingStrategy): { + seek(position: any): Promise; + truncate(size: any): Promise; + write(data: any): Promise; + readonly locked: boolean; + abort(reason?: any): Promise; + close(): Promise; + getWriter(): WritableStreamDefaultWriter; + }; + }; + namespace _default { + export { createFileSystemWritableFileStream }; + export { createFileSystemDirectoryHandle }; + export { createFileSystemFileHandle }; + export { createFile }; + } + export default _default; + import fs from "fs/promises"; +} +declare module "extension" { + /** + * Load an extension by name. + * @template {Record T} + * @param {string} name + * @param {ExtensionLoadOptions} [options] + * @return {Promise>} + */ + export function load>(name: string, options?: ExtensionLoadOptions): Promise>; + /** + * Provides current stats about the loaded extensions. + * @return {Promise} + */ + export function stats(): Promise; + /** + * @typedef {{ + * allow: string[] | string, + * imports?: object, + * type?: 'shared' | 'wasm32', + * path?: string, + * stats?: object, + * instance?: WebAssembly.Instance, + * adapter?: WebAssemblyExtensionAdapter + * }} ExtensionLoadOptions + */ + /** + * @typedef {{ abi: number, version: string, description: string }} ExtensionInfo + */ + /** + * @typedef {{ abi: number, loaded: number }} ExtensionStats + */ + /** + * A interface for a native extension. + * @template {Record T} + */ + export class Extension> extends EventTarget { + /** + * Load an extension by name. + * @template {Record T} + * @param {string} name + * @param {ExtensionLoadOptions} [options] + * @return {Promise>} + */ + static load>(name: string, options?: ExtensionLoadOptions): Promise>; + /** + * Query type of extension by name. + * @param {string} name + * @return {Promise<'shared'|'wasm32'|'unknown'|null>} + */ + static type(name: string): Promise<"shared" | "wasm32" | "unknown" | null>; + /** + * Provides current stats about the loaded extensions or one by name. + * @param {?string} name + * @return {Promise} + */ + static stats(name: string | null): Promise; + /** + * `Extension` class constructor. + * @param {string} name + * @param {ExtensionInfo} info + * @param {ExtensionLoadOptions} [options] + */ + constructor(name: string, info: ExtensionInfo, options?: ExtensionLoadOptions); + /** + * The name of the extension + * @type {string?} + */ + name: string | null; + /** + * The version of the extension + * @type {string?} + */ + version: string | null; + /** + * The description of the extension + * @type {string?} + */ + description: string | null; + /** + * The abi of the extension + * @type {number} + */ + abi: number; + /** + * @type {object} + */ + options: object; + /** + * @type {T} + */ + binding: T; + /** + * Not `null` if extension is of type 'wasm32' + * @type {?WebAssemblyExtensionAdapter} + */ + adapter: WebAssemblyExtensionAdapter | null; + /** + * `true` if the extension was loaded, otherwise `false` + * @type {boolean} + */ + get loaded(): boolean; + /** + * The extension type: 'shared' or 'wasm32' + * @type {'shared'|'wasm32'} + */ + get type(): "shared" | "wasm32"; + /** + * Unloads the loaded extension. + * @throws Error + */ + unload(): Promise; + instance: any; + [$type]: "shared" | "wasm32"; + [$loaded]: boolean; + } + namespace _default { + export { load }; + export { stats }; + } + export default _default; + export type Pointer = number; + export type ExtensionLoadOptions = { + allow: string[] | string; + imports?: object; + type?: "shared" | "wasm32"; + path?: string; + stats?: object; + instance?: WebAssembly.Instance; + adapter?: WebAssemblyExtensionAdapter; + }; + export type ExtensionInfo = { + abi: number; + version: string; + description: string; + }; + export type ExtensionStats = { + abi: number; + loaded: number; + }; + /** + * An adapter for reading and writing various values from a WebAssembly instance's + * memory buffer. + * @ignore + */ + class WebAssemblyExtensionAdapter { + constructor({ instance, module, table, memory, policies }: { + instance: any; + module: any; + table: any; + memory: any; + policies: any; + }); + view: any; + heap: any; + table: any; + stack: any; + buffer: any; + module: any; + memory: any; + context: any; + policies: any[]; + externalReferences: Map; + instance: any; + exitStatus: any; + textDecoder: TextDecoder; + textEncoder: TextEncoder; + errorMessagePointers: {}; + indirectFunctionTable: any; + get globalBaseOffset(): any; + destroy(): void; + init(): boolean; + get(pointer: any, size?: number): any; + set(pointer: any, value: any): void; + createExternalReferenceValue(value: any): any; + getExternalReferenceValue(pointer: any): any; + setExternalReferenceValue(pointer: any, value: any): Map; + removeExternalReferenceValue(pointer: any): void; + getExternalReferencePointer(value: any): any; + getFloat32(pointer: any): any; + setFloat32(pointer: any, value: any): boolean; + getFloat64(pointer: any): any; + setFloat64(pointer: any, value: any): boolean; + getInt8(pointer: any): any; + setInt8(pointer: any, value: any): boolean; + getInt16(pointer: any): any; + setInt16(pointer: any, value: any): boolean; + getInt32(pointer: any): any; + setInt32(pointer: any, value: any): boolean; + getUint8(pointer: any): any; + setUint8(pointer: any, value: any): boolean; + getUint16(pointer: any): any; + setUint16(pointer: any, value: any): boolean; + getUint32(pointer: any): any; + setUint32(pointer: any, value: any): boolean; + getString(pointer: any, buffer: any, size: any): string; + setString(pointer: any, string: any, buffer?: any): boolean; + } + const $type: unique symbol; + /** + * @typedef {number} Pointer + */ + const $loaded: unique symbol; +} +declare module "internal/database" { + /** + * A typed container for optional options given to the `Database` + * class constructor. + * + * @typedef {{ + * version?: string | undefined + * }} DatabaseOptions + */ + /** + * A typed container for various optional options made to a `get()` function + * on a `Database` instance. + * + * @typedef {{ + * store?: string | undefined, + * stores?: string[] | undefined, + * count?: number | undefined + * }} DatabaseGetOptions + */ + /** + * A typed container for various optional options made to a `put()` function + * on a `Database` instance. + * + * @typedef {{ + * store?: string | undefined, + * stores?: string[] | undefined, + * durability?: 'strict' | 'relaxed' | undefined + * }} DatabasePutOptions + */ + /** + * A typed container for various optional options made to a `delete()` function + * on a `Database` instance. + * + * @typedef {{ + * store?: string | undefined, + * stores?: string[] | undefined + * }} DatabaseDeleteOptions + */ + /** + * A typed container for optional options given to the `Database` + * class constructor. + * + * @typedef {{ + * offset?: number | undefined, + * backlog?: number | undefined + * }} DatabaseRequestQueueWaitOptions + */ + /** + * A typed container for various optional options made to a `entries()` function + * on a `Database` instance. + * + * @typedef {{ + * store?: string | undefined, + * stores?: string[] | undefined + * }} DatabaseEntriesOptions + */ + /** + * A `DatabaseRequestQueueRequestConflict` callback function type. + * @typedef {function(Event, DatabaseRequestQueueRequestConflict): any} DatabaseRequestQueueConflictResolutionCallback + */ + /** + * Waits for an event of `eventType` to be dispatched on a given `EventTarget`. + * @param {EventTarget} target + * @param {string} eventType + * @return {Promise} + */ + export function waitFor(target: EventTarget, eventType: string): Promise; + /** + * Creates an opens a named `Database` instance. + * @param {string} name + * @param {?DatabaseOptions | undefined} [options] + * @return {Promise} + */ + export function open(name: string, options?: (DatabaseOptions | undefined) | null): Promise; + /** + * Complete deletes a named `Database` instance. + * @param {string} name + * @param {?DatabaseOptions|undefined} [options] + */ + export function drop(name: string, options?: (DatabaseOptions | undefined) | null): Promise; + /** + * A mapping of named `Database` instances that are currently opened + * @type {Map>} + */ + export const opened: Map>; + /** + * A container for conflict resolution for a `DatabaseRequestQueue` instance + * `IDBRequest` instance. + */ + export class DatabaseRequestQueueRequestConflict { + /** + * `DatabaseRequestQueueRequestConflict` class constructor + * @param {function(any): void)} resolve + * @param {function(Error): void)} reject + * @param {function(): void)} cleanup + */ + constructor(resolve: any, reject: any, cleanup: any); + /** + * Called when a conflict is resolved. + * @param {any} argument + */ + resolve(argument?: any): void; + /** + * Called when a conflict is rejected + * @param {Error} error + */ + reject(error: Error): void; + #private; + } + /** + * An event dispatched on a `DatabaseRequestQueue` + */ + export class DatabaseRequestQueueEvent extends Event { + /** + * `DatabaseRequestQueueEvent` class constructor. + * @param {string} type + * @param {IDBRequest|IDBTransaction} request + */ + constructor(type: string, request: IDBRequest | IDBTransaction); + /** + * A reference to the underlying request for this event. + * @type {IDBRequest|IDBTransaction} + */ + get request(): IDBRequest | IDBTransaction; + #private; + } + /** + * An event dispatched on a `Database` + */ + export class DatabaseEvent extends Event { + /** + * `DatabaseEvent` class constructor. + * @param {string} type + * @param {Database} database + */ + constructor(type: string, database: Database); + /** + * A reference to the underlying database for this event. + * @type {Database} + */ + get database(): Database; + #private; + } + /** + * An error event dispatched on a `DatabaseRequestQueue` + */ + export class DatabaseRequestQueueErrorEvent extends ErrorEvent { + /** + * `DatabaseRequestQueueErrorEvent` class constructor. + * @param {string} type + * @param {IDBRequest|IDBTransaction} request + * @param {{ error: Error, cause?: Error }} options + */ + constructor(type: string, request: IDBRequest | IDBTransaction, options: { + error: Error; + cause?: Error; + }); + /** + * A reference to the underlying request for this error event. + * @type {IDBRequest|IDBTransaction} + */ + get request(): IDBRequest | IDBTransaction; + #private; + } + /** + * A container for various `IDBRequest` and `IDBTransaction` instances + * occurring during the life cycles of a `Database` instance. + */ + export class DatabaseRequestQueue extends EventTarget { + /** + * Computed queue length + * @type {number} + */ + get length(): number; + /** + * Pushes an `IDBRequest` or `IDBTransaction onto the queue and returns a + * `Promise` that resolves upon a 'success' or 'complete' event and rejects + * upon an error' event. + * @param {IDBRequest|IDBTransaction} + * @param {?DatabaseRequestQueueConflictResolutionCallback} [conflictResolutionCallback] + * @return {Promise} + */ + push(request: any, conflictResolutionCallback?: DatabaseRequestQueueConflictResolutionCallback | null): Promise; + /** + * Waits for all pending requests to complete. This function will throw when + * an `IDBRequest` or `IDBTransaction` instance emits an 'error' event. + * Callers of this function can optionally specify a maximum backlog to wait + * for instead of waiting for all requests to finish. + * @param {?DatabaseRequestQueueWaitOptions | undefined} [options] + */ + wait(options?: (DatabaseRequestQueueWaitOptions | undefined) | null): Promise; + #private; + } + /** + * An interface for reading from named databases backed by IndexedDB. + */ + export class Database extends EventTarget { + /** + * `Database` class constructor. + * @param {string} name + * @param {?DatabaseOptions | undefined} [options] + */ + constructor(name: string, options?: (DatabaseOptions | undefined) | null); + /** + * `true` if the `Database` is currently opening, otherwise `false`. + * A `Database` instance should not attempt to be opened if this property value + * is `true`. + * @type {boolean} + */ + get opening(): boolean; + /** + * `true` if the `Database` instance was successfully opened such that the + * internal `IDBDatabase` storage instance was created and can be referenced + * on the `Database` instance, otherwise `false`. + * @type {boolean} + */ + get opened(): boolean; + /** + * `true` if the `Database` instance was closed or has not been opened such + * that the internal `IDBDatabase` storage instance was not created or cannot + * be referenced on the `Database` instance, otherwise `false`. + * @type {boolean} + */ + get closed(): boolean; + /** + * `true` if the `Database` is currently closing, otherwise `false`. + * A `Database` instance should not attempt to be closed if this property value + * is `true`. + * @type {boolean} + */ + get closing(): boolean; + /** + * The name of the `IDBDatabase` database. This value cannot be `null`. + * @type {string} + */ + get name(): string; + /** + * The version of the `IDBDatabase` database. This value may be `null`. + * @type {?string} + */ + get version(): string; + /** + * A reference to the `IDBDatabase`, if the `Database` instance was opened. + * This value may ba `null`. + * @type {?IDBDatabase} + */ + get storage(): IDBDatabase; + /** + * Opens the `IDBDatabase` database optionally at a specific "version" if + * one was given upon construction of the `Database` instance. This function + * is not idempotent and will throw if the underlying `IDBDatabase` instance + * was created successfully or is in the process of opening. + * @return {Promise} + */ + open(): Promise; + /** + * Closes the `IDBDatabase` database storage, if opened. This function is not + * idempotent and will throw if the underlying `IDBDatabase` instance is + * already closed (not opened) or currently closing. + * @return {Promise} + */ + close(): Promise; + /** + * Deletes entire `Database` instance and closes after successfully + * delete storage. + */ + drop(): Promise; + /** + * Gets a "readonly" value by `key` in the `Database` object storage. + * @param {string} key + * @param {?DatabaseGetOptions|undefined} [options] + * @return {Promise} + */ + get(key: string, options?: (DatabaseGetOptions | undefined) | null): Promise; + /** + * Put a `value` at `key`, updating if it already exists, otherwise + * "inserting" it into the `Database` instance. + * @param {string} key + * @param {any} value + * @param {?DatabasePutOptions|undefined} [options] + * @return {Promise} + */ + put(key: string, value: any, options?: (DatabasePutOptions | undefined) | null): Promise; + /** + * Inserts a new `value` at `key`. This function throws if a value at `key` + * already exists. + * @param {string} key + * @param {any} value + * @param {?DatabasePutOptions|undefined} [options] + * @return {Promise} + */ + insert(key: string, value: any, options?: (DatabasePutOptions | undefined) | null): Promise; + /** + * Update a `value` at `key`, updating if it already exists, otherwise + * "inserting" it into the `Database` instance. + * @param {string} key + * @param {any} value + * @param {?DatabasePutOptions|undefined} [options] + * @return {Promise} + */ + update(key: string, value: any, options?: (DatabasePutOptions | undefined) | null): Promise; + /** + * Delete a value at `key`. + * @param {string} key + * @param {?DatabaseDeleteOptions|undefined} [options] + * @return {Promise} + */ + delete(key: string, options?: (DatabaseDeleteOptions | undefined) | null): Promise; + /** + * Gets a "readonly" value by `key` in the `Database` object storage. + * @param {?DatabaseEntriesOptions|undefined} [options] + * @return {Promise} + */ + entries(options?: (DatabaseEntriesOptions | undefined) | null): Promise; + #private; + } + namespace _default { + export { Database }; + export { open }; + export { drop }; + } + export default _default; + /** + * A typed container for optional options given to the `Database` + * class constructor. + */ + export type DatabaseOptions = { + version?: string | undefined; + }; + /** + * A typed container for various optional options made to a `get()` function + * on a `Database` instance. + */ + export type DatabaseGetOptions = { + store?: string | undefined; + stores?: string[] | undefined; + count?: number | undefined; + }; + /** + * A typed container for various optional options made to a `put()` function + * on a `Database` instance. + */ + export type DatabasePutOptions = { + store?: string | undefined; + stores?: string[] | undefined; + durability?: "strict" | "relaxed" | undefined; + }; + /** + * A typed container for various optional options made to a `delete()` function + * on a `Database` instance. + */ + export type DatabaseDeleteOptions = { + store?: string | undefined; + stores?: string[] | undefined; + }; + /** + * A typed container for optional options given to the `Database` + * class constructor. + */ + export type DatabaseRequestQueueWaitOptions = { + offset?: number | undefined; + backlog?: number | undefined; + }; + /** + * A typed container for various optional options made to a `entries()` function + * on a `Database` instance. + */ + export type DatabaseEntriesOptions = { + store?: string | undefined; + stores?: string[] | undefined; + }; + /** + * A `DatabaseRequestQueueRequestConflict` callback function type. + */ + export type DatabaseRequestQueueConflictResolutionCallback = (arg0: Event, arg1: DatabaseRequestQueueRequestConflict) => any; +} +declare module "service-worker/env" { + /** + * Opens an environment for a particular scope. + * @param {EnvironmentOptions} options + * @return {Promise} + */ + export function open(options: EnvironmentOptions): Promise; + /** + * Closes an active `Environment` instance, dropping the global + * instance reference. + * @return {Promise} + */ + export function close(): Promise; + /** + * Resets an active `Environment` instance + * @return {Promise} + */ + export function reset(): Promise; + /** + * @typedef {{ + * scope: string + * }} EnvironmentOptions + */ + /** + * An event dispatched when a environment value is updated (set, delete) + */ + export class EnvironmentEvent extends Event { + /** + * `EnvironmentEvent` class constructor. + * @param {'set'|'delete'} type + * @param {object=} [entry] + */ + constructor(type: "set" | "delete", entry?: object | undefined); + entry: any; + } + /** + * An environment context object with persistence and durability + * for service worker environments. + */ + export class Environment extends EventTarget { + /** + * Maximum entries that will be restored from storage into the environment + * context object. + * @type {number} + */ + static MAX_CONTEXT_ENTRIES: number; + /** + * Opens an environment for a particular scope. + * @param {EnvironmentOptions} options + * @return {Environment} + */ + static open(options: EnvironmentOptions): Environment; + /** + * The current `Environment` instance + * @type {Environment?} + */ + static instance: Environment | null; + /** + * `Environment` class constructor + * @ignore + * @param {EnvironmentOptions} options + */ + constructor(options: EnvironmentOptions); + /** + * A reference to the currently opened environment database. + * @type {import('../internal/database.js').Database} + */ + get database(): import("internal/database").Database; + /** + * A proxied object for reading and writing environment state. + * Values written to this object must be cloneable with respect to the + * structured clone algorithm. + * @see {https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm} + * @type {Proxy} + */ + get context(): ProxyConstructor; + /** + * The environment type + * @type {string} + */ + get type(): string; + /** + * The current environment name. This value is also used as the + * internal database name. + * @type {string} + */ + get name(): string; + /** + * Resets the current environment to an empty state. + */ + reset(): Promise; + /** + * Opens the environment. + * @ignore + */ + open(): Promise; + /** + * Closes the environment database, purging existing state. + * @ignore + */ + close(): Promise; + #private; + } + namespace _default { + export { Environment }; + export { close }; + export { reset }; + export { open }; + } + export default _default; + export type EnvironmentOptions = { + scope: string; + }; +} +declare module "service-worker/debug" { + export function debug(...args: any[]): void; + export default debug; +} +declare module "service-worker/state" { + export const channel: ipc.IPCBroadcastChannel; + export const state: any; + export default state; + import ipc from "ipc"; +} +declare module "service-worker/clients" { + export class Client { + constructor(options: any); + get id(): any; + get url(): any; + get type(): any; + get frameType(): any; + postMessage(message: any, optionsOrTransferables?: any): void; + #private; + } + export class WindowClient extends Client { + get focused(): boolean; + get ancestorOrigins(): any[]; + get visibilityState(): string; + focus(): Promise; + navigate(url: any): Promise; + #private; + } + export class Clients { + get(id: any): Promise; + matchAll(options?: any): Promise; + openWindow(url: any, options?: any): Promise; + claim(): Promise; + } + const _default: Clients; + export default _default; +} +declare module "service-worker/context" { + /** + * A context given to `ExtendableEvent` interfaces and provided to + * simplified service worker modules + */ + export class Context { + /** + * `Context` class constructor. + * @param {import('./events.js').ExtendableEvent} event + */ + constructor(event: import("service-worker/events").ExtendableEvent); + /** + * Context data. This may be a custom protocol handler scheme data + * by default, if available. + * @type {any?} + */ + data: any | null; + /** + * The `ExtendableEvent` for this `Context` instance. + * @type {ExtendableEvent} + */ + get event(): ExtendableEvent; + /** + * An environment context object. + * @type {object?} + */ + get env(): any; + /** + * Resets the current environment context. + * @return {Promise} + */ + resetEnvironment(): Promise; + /** + * Unused, but exists for cloudflare compat. + * @ignore + */ + passThroughOnException(): void; + /** + * Tells the event dispatcher that work is ongoing. + * It can also be used to detect whether that work was successful. + * @param {Promise} promise + */ + waitUntil(promise: Promise): Promise; + /** + * TODO + */ + handled(): Promise; + /** + * Gets the client for this event context. + * @return {Promise} + */ + client(): Promise; + #private; + } + namespace _default { + export { Context }; + } + export default _default; +} +declare module "service-worker/events" { + export const textEncoder: TextEncoderStream; + export const FETCH_EVENT_TIMEOUT: number; + export const FETCH_EVENT_MAX_RESPONSE_REDIRECTS: number; + /** + * The `ExtendableEvent` interface extends the lifetime of the "install" and + * "activate" events dispatched on the global scope as part of the service + * worker lifecycle. + */ + export class ExtendableEvent extends Event { + /** + * `ExtendableEvent` class constructor. + * @ignore + */ + constructor(...args: any[]); + /** + * A context for this `ExtendableEvent` instance. + * @type {import('./context.js').Context} + */ + get context(): Context; + /** + * A promise that can be awaited which waits for this `ExtendableEvent` + * instance no longer has pending promises. + * @type {Promise} + */ + get awaiting(): Promise; + /** + * The number of pending promises + * @type {number} + */ + get pendingPromises(): number; + /** + * `true` if the `ExtendableEvent` instance is considered "active", + * otherwise `false`. + * @type {boolean} + */ + get isActive(): boolean; + /** + * Tells the event dispatcher that work is ongoing. + * It can also be used to detect whether that work was successful. + * @param {Promise} promise + */ + waitUntil(promise: Promise): void; + /** + * Returns a promise that this `ExtendableEvent` instance is waiting for. + * @return {Promise} + */ + waitsFor(): Promise; + #private; + } + /** + * This is the event type for "fetch" events dispatched on the service worker + * global scope. It contains information about the fetch, including the + * request and how the receiver will treat the response. + */ + export class FetchEvent extends ExtendableEvent { + static defaultHeaders: Headers; + /** + * `FetchEvent` class constructor. + * @ignore + * @param {string=} [type = 'fetch'] + * @param {object=} [options] + */ + constructor(type?: string | undefined, options?: object | undefined); + /** + * The handled property of the `FetchEvent` interface returns a promise + * indicating if the event has been handled by the fetch algorithm or not. + * This property allows executing code after the browser has consumed a + * response, and is usually used together with the `waitUntil()` method. + * @type {Promise} + */ + get handled(): Promise; + /** + * The request read-only property of the `FetchEvent` interface returns the + * `Request` that triggered the event handler. + * @type {Request} + */ + get request(): Request; + /** + * The `clientId` read-only property of the `FetchEvent` interface returns + * the id of the Client that the current service worker is controlling. + * @type {string} + */ + get clientId(): string; + /** + * @ignore + * @type {string} + */ + get resultingClientId(): string; + /** + * @ignore + * @type {string} + */ + get replacesClientId(): string; + /** + * @ignore + * @type {boolean} + */ + get isReload(): boolean; + /** + * @ignore + * @type {Promise} + */ + get preloadResponse(): Promise; + /** + * The `respondWith()` method of `FetchEvent` prevents the webview's + * default fetch handling, and allows you to provide a promise for a + * `Response` yourself. + * @param {Response|Promise} response + */ + respondWith(response: Response | Promise): void; + #private; + } + export class ExtendableMessageEvent extends ExtendableEvent { + /** + * `ExtendableMessageEvent` class constructor. + * @param {string=} [type = 'message'] + * @param {object=} [options] + */ + constructor(type?: string | undefined, options?: object | undefined); + /** + * @type {any} + */ + get data(): any; + /** + * @type {MessagePort[]} + */ + get ports(): MessagePort[]; + /** + * @type {import('./clients.js').Client?} + */ + get source(): import("service-worker/clients").Client; + /** + * @type {string?} + */ + get origin(): string; + /** + * @type {string} + */ + get lastEventId(): string; + #private; + } + export class NotificationEvent extends ExtendableEvent { + constructor(type: any, options: any); + get action(): string; + get notification(): any; + #private; + } + namespace _default { + export { ExtendableMessageEvent }; + export { ExtendableEvent }; + export { FetchEvent }; + } + export default _default; + import { Context } from "service-worker/context"; +} +declare module "http/adapters" { + /** + * @typedef {{ + * Connection: typeof import('../http.js').Connection, + * globalAgent: import('../http.js').Agent, + * IncomingMessage: typeof import('../http.js').IncomingMessage, + * ServerResponse: typeof import('../http.js').ServerResponse, + * STATUS_CODES: object, + * METHODS: string[] + * }} HTTPModuleInterface + */ + /** + * An abstract base clase for a HTTP server adapter. + */ + export class ServerAdapter extends EventTarget { + /** + * `ServerAdapter` class constructor. + * @ignore + * @param {import('../http.js').Server} server + * @param {HTTPModuleInterface} httpInterface + */ + constructor(server: import("http").Server, httpInterface: HTTPModuleInterface); + /** + * A readonly reference to the underlying HTTP(S) server + * for this adapter. + * @type {import('../http.js').Server} + */ + get server(): import("http").Server; + /** + * A readonly reference to the underlying HTTP(S) module interface + * for creating various HTTP module class objects. + * @type {HTTPModuleInterface} + */ + get httpInterface(): HTTPModuleInterface; + /** + * A readonly reference to the `AsyncContext.Variable` associated with this + * `ServerAdapter` instance. + */ + get context(): import("async/context").Variable; + /** + * Called when the adapter should destroy itself. + * @abstract + */ + destroy(): Promise; + #private; + } + /** + * A HTTP adapter for running a HTTP server in a service worker that uses the + * "fetch" event for the request and response lifecycle. + */ + export class ServiceWorkerServerAdapter extends ServerAdapter { + /** + * Handles the 'install' service worker event. + * @ignore + * @param {import('../service-worker/events.js').ExtendableEvent} event + */ + onInstall(event: import("service-worker/events").ExtendableEvent): Promise; + /** + * Handles the 'activate' service worker event. + * @ignore + * @param {import('../service-worker/events.js').ExtendableEvent} event + */ + onActivate(event: import("service-worker/events").ExtendableEvent): Promise; + /** + * Handles the 'fetch' service worker event. + * @ignore + * @param {import('../service-worker/events.js').FetchEvent} + */ + onFetch(event: any): Promise; + } + namespace _default { + export { ServerAdapter }; + export { ServiceWorkerServerAdapter }; + } + export default _default; + export type HTTPModuleInterface = { + Connection: typeof import("http").Connection; + globalAgent: import("http").Agent; + IncomingMessage: typeof import("http").IncomingMessage; + ServerResponse: typeof import("http").ServerResponse; + STATUS_CODES: object; + METHODS: string[]; + }; +} +declare module "http" { + /** + * Makes a HTTP or `socket:` GET request. A simplified alias to `request()`. + * @param {string|object} optionsOrURL + * @param {(object|function)=} [options] + * @param {function=} [callback] + * @return {ClientRequest} + */ + export function get(optionsOrURL: string | object, options?: (object | Function) | undefined, callback?: Function | undefined): ClientRequest; + /** + * Creates a HTTP server that can listen for incoming requests. + * Requests that are dispatched to this server depend on the context + * in which it is created, such as a service worker which will use a + * "fetch event" adapter. + * @param {object|function=} [options] + * @param {function=} [callback] + * @return {Server} + */ + export function createServer(options?: (object | Function) | undefined, callback?: Function | undefined): Server; + /** + * All known possible HTTP methods. + * @type {string[]} + */ + export const METHODS: string[]; + /** + * A mapping of status codes to status texts + * @type {object} + */ + export const STATUS_CODES: object; + export const CONTINUE: 100; + export const SWITCHING_PROTOCOLS: 101; + export const PROCESSING: 102; + export const EARLY_HINTS: 103; + export const OK: 200; + export const CREATED: 201; + export const ACCEPTED: 202; + export const NONAUTHORITATIVE_INFORMATION: 203; + export const NO_CONTENT: 204; + export const RESET_CONTENT: 205; + export const PARTIAL_CONTENT: 206; + export const MULTISTATUS: 207; + export const ALREADY_REPORTED: 208; + export const IM_USED: 226; + export const MULTIPLE_CHOICES: 300; + export const MOVED_PERMANENTLY: 301; + export const FOUND: 302; + export const SEE_OTHER: 303; + export const NOT_MODIFIED: 304; + export const USE_PROXY: 305; + export const TEMPORARY_REDIRECT: 307; + export const PERMANENT_REDIRECT: 308; + export const BAD_REQUEST: 400; + export const UNAUTHORIZED: 401; + export const PAYMENT_REQUIRED: 402; + export const FORBIDDEN: 403; + export const NOT_FOUND: 404; + export const METHOD_NOT_ALLOWED: 405; + export const NOT_ACCEPTABLE: 406; + export const PROXY_AUTHENTICATION_REQUIRED: 407; + export const REQUEST_TIMEOUT: 408; + export const CONFLICT: 409; + export const GONE: 410; + export const LENGTH_REQUIRED: 411; + export const PRECONDITION_FAILED: 412; + export const PAYLOAD_TOO_LARGE: 413; + export const URI_TOO_LONG: 414; + export const UNSUPPORTED_MEDIA_TYPE: 415; + export const RANGE_NOT_SATISFIABLE: 416; + export const EXPECTATION_FAILED: 417; + export const IM_A_TEAPOT: 418; + export const MISDIRECTED_REQUEST: 421; + export const UNPROCESSABLE_ENTITY: 422; + export const LOCKED: 423; + export const FAILED_DEPENDENCY: 424; + export const TOO_EARLY: 425; + export const UPGRADE_REQUIRED: 426; + export const PRECONDITION_REQUIRED: 428; + export const TOO_MANY_REQUESTS: 429; + export const REQUEST_HEADER_FIELDS_TOO_LARGE: 431; + export const UNAVAILABLE_FOR_LEGAL_REASONS: 451; + export const INTERNAL_SERVER_ERROR: 500; + export const NOT_IMPLEMENTED: 501; + export const BAD_GATEWAY: 502; + export const SERVICE_UNAVAILABLE: 503; + export const GATEWAY_TIMEOUT: 504; + export const HTTP_VERSION_NOT_SUPPORTED: 505; + export const VARIANT_ALSO_NEGOTIATES: 506; + export const INSUFFICIENT_STORAGE: 507; + export const LOOP_DETECTED: 508; + export const BANDWIDTH_LIMIT_EXCEEDED: 509; + export const NOT_EXTENDED: 510; + export const NETWORK_AUTHENTICATION_REQUIRED: 511; + /** + * The parent class of `ClientRequest` and `ServerResponse`. + * It is an abstract outgoing message from the perspective of the + * participants of an HTTP transaction. + * @see {@link https://nodejs.org/api/http.html#class-httpoutgoingmessage} + */ + export class OutgoingMessage extends Writable { + /** + * `OutgoingMessage` class constructor. + * @ignore + */ + constructor(); + /** + * `true` if the headers were sent + * @type {boolean} + */ + headersSent: boolean; + /** + * Internal buffers + * @ignore + * @type {Buffer[]} + */ + get buffers(): Buffer[]; + /** + * An object of the outgoing message headers. + * This is equivalent to `getHeaders()` + * @type {object} + */ + get headers(): any; + /** + * @ignore + */ + get socket(): this; + /** + * `true` if the write state is "ended" + * @type {boolean} + */ + get writableEnded(): boolean; + /** + * `true` if the write state is "finished" + * @type {boolean} + */ + get writableFinished(): boolean; + /** + * The number of buffered bytes. + * @type {number} + */ + get writableLength(): number; + /** + * @ignore + * @type {boolean} + */ + get writableObjectMode(): boolean; + /** + * @ignore + */ + get writableCorked(): number; + /** + * The `highWaterMark` of the writable stream. + * @type {number} + */ + get writableHighWaterMark(): number; + /** + * @ignore + * @return {OutgoingMessage} + */ + addTrailers(headers: any): OutgoingMessage; + /** + * @ignore + * @return {OutgoingMessage} + */ + cork(): OutgoingMessage; + /** + * @ignore + * @return {OutgoingMessage} + */ + uncork(): OutgoingMessage; + /** + * Destroys the message. + * Once a socket is associated with the message and is connected, + * that socket will be destroyed as well. + * @param {Error?} [err] + * @return {OutgoingMessage} + */ + destroy(err?: Error | null): OutgoingMessage; + /** + * Finishes the outgoing message. + * @param {(Buffer|Uint8Array|string|function)=} [chunk] + * @param {(string|function)=} [encoding] + * @param {function=} [callback] + * @return {OutgoingMessage} + */ + end(chunk?: (Buffer | Uint8Array | string | Function) | undefined, encoding?: (string | Function) | undefined, callback?: Function | undefined): OutgoingMessage; + /** + * Append a single header value for the header object. + * @param {string} name + * @param {string|string[]} value + * @return {OutgoingMessage} + */ + appendHeader(name: string, value: string | string[]): OutgoingMessage; + /** + * Append a single header value for the header object. + * @param {string} name + * @param {string} value + * @return {OutgoingMessage} + */ + setHeader(name: string, value: string): OutgoingMessage; + /** + * Flushes the message headers. + */ + flushHeaders(): void; + /** + * Gets the value of the HTTP header with the given name. + * If that header is not set, the returned value will be `undefined`. + * @param {string} + * @return {string|undefined} + */ + getHeader(name: any): string | undefined; + /** + * Returns an array containing the unique names of the current outgoing + * headers. All names are lowercase. + * @return {string[]} + */ + getHeaderNames(): string[]; + /** + * @ignore + */ + getRawHeaderNames(): string[]; + /** + * Returns a copy of the HTTP headers as an object. + * @return {object} + */ + getHeaders(): object; + /** + * Returns true if the header identified by name is currently set in the + * outgoing headers. The header name is case-insensitive. + * @param {string} name + * @return {boolean} + */ + hasHeader(name: string): boolean; + /** + * Removes a header that is queued for implicit sending. + * @param {string} name + */ + removeHeader(name: string): void; + /** + * Sets the outgoing message timeout with an optional callback. + * @param {number} timeout + * @param {function=} [callback] + * @return {OutgoingMessage} + */ + setTimeout(timeout: number, callback?: Function | undefined): OutgoingMessage; + /** + * @ignore + */ + _implicitHeader(): void; + #private; + } + /** + * An `IncomingMessage` object is created by `Server` or `ClientRequest` and + * passed as the first argument to the 'request' and 'response' event + * respectively. + * It may be used to access response status, headers, and data. + * @see {@link https://nodejs.org/api/http.html#class-httpincomingmessage} + */ + export class IncomingMessage extends Readable { + /** + * `IncomingMessage` class constructor. + * @ignore + * @param {object} options + */ + constructor(options: object); + set url(url: string); + /** + * The URL for this incoming message. This value is not absolute with + * respect to the protocol and hostname. It includes the path and search + * query component parameters. + * @type {string} + */ + get url(): string; + /** + * @type {Server} + */ + get server(): exports.Server; + /** + * @type {AsyncContext.Variable} + */ + get context(): typeof import("async/context").Variable; + /** + * This property will be `true` if a complete HTTP message has been received + * and successfully parsed. + * @type {boolean} + */ + get complete(): boolean; + /** + * An object of the incoming message headers. + * @type {object} + */ + get headers(): any; + /** + * Similar to `message.headers`, but there is no join logic and the values + * are always arrays of strings, even for headers received just once. + * @type {object} + */ + get headersDistinct(): any; + /** + * The HTTP major version of this request. + * @type {number} + */ + get httpVersionMajor(): number; + /** + * The HTTP minor version of this request. + * @type {number} + */ + get httpVersionMinor(): number; + /** + * The HTTP version string. + * A concatenation of `httpVersionMajor` and `httpVersionMinor`. + * @type {string} + */ + get httpVersion(): string; + /** + * The HTTP request method. + * @type {string} + */ + get method(): string; + /** + * The raw request/response headers list potentially as they were received. + * @type {string[]} + */ + get rawHeaders(): string[]; + /** + * @ignore + */ + get rawTrailers(): any[]; + /** + * @ignore + */ + get socket(): this; + /** + * The HTTP request status code. + * Only valid for response obtained from `ClientRequest`. + * @type {number} + */ + get statusCode(): number; + /** + * The HTTP response status message (reason phrase). + * Such as "OK" or "Internal Server Error." + * Only valid for response obtained from `ClientRequest`. + * @type {string?} + */ + get statusMessage(): string; + /** + * An alias for `statusCode` + * @type {number} + */ + get status(): number; + /** + * An alias for `statusMessage` + * @type {string?} + */ + get statusText(): string; + /** + * @ignore + */ + get trailers(): {}; + /** + * @ignore + */ + get trailersDistinct(): {}; + /** + * Gets the value of the HTTP header with the given name. + * If that header is not set, the returned value will be `undefined`. + * @param {string} + * @return {string|undefined} + */ + getHeader(name: any): string | undefined; + /** + * Returns an array containing the unique names of the current outgoing + * headers. All names are lowercase. + * @return {string[]} + */ + getHeaderNames(): string[]; + /** + * @ignore + */ + getRawHeaderNames(): string[]; + /** + * Returns a copy of the HTTP headers as an object. + * @return {object} + */ + getHeaders(): object; + /** + * Returns true if the header identified by name is currently set in the + * outgoing headers. The header name is case-insensitive. + * @param {string} name + * @return {boolean} + */ + hasHeader(name: string): boolean; + /** + * Sets the incoming message timeout with an optional callback. + * @param {number} timeout + * @param {function=} [callback] + * @return {IncomingMessage} + */ + setTimeout(timeout: number, callback?: Function | undefined): IncomingMessage; + #private; + } + /** + * An object that is created internally and returned from `request()`. + * @see {@link https://nodejs.org/api/http.html#class-httpclientrequest} + */ + export class ClientRequest extends exports.OutgoingMessage { + /** + * `ClientRequest` class constructor. + * @ignore + * @param {object} options + */ + constructor(options: object); + /** + * The HTTP request method. + * @type {string} + */ + get method(): string; + /** + * The request protocol + * @type {string?} + */ + get protocol(): string; + /** + * The request path. + * @type {string} + */ + get path(): string; + /** + * The request host name (including port). + * @type {string?} + */ + get host(): string; + /** + * The URL for this outgoing message. This value is not absolute with + * respect to the protocol and hostname. It includes the path and search + * query component parameters. + * @type {string} + */ + get url(): string; + /** + * @ignore + * @type {boolean} + */ + get finished(): boolean; + /** + * @ignore + * @type {boolean} + */ + get reusedSocket(): boolean; + /** + * @ignore + * @param {boolean=} [value] + * @return {ClientRequest} + */ + setNoDelay(value?: boolean | undefined): ClientRequest; + /** + * @ignore + * @param {boolean=} [enable] + * @param {number=} [initialDelay] + * @return {ClientRequest} + */ + setSocketKeepAlive(enable?: boolean | undefined, initialDelay?: number | undefined): ClientRequest; + #private; + } + /** + * An object that is created internally by a `Server` instance, not by the user. + * It is passed as the second parameter to the 'request' event. + * @see {@link https://nodejs.org/api/http.html#class-httpserverresponse} + */ + export class ServerResponse extends exports.OutgoingMessage { + /** + * `ServerResponse` class constructor. + * @param {object} options + */ + constructor(options: object); + /** + * @type {Server} + */ + get server(): exports.Server; + /** + * A reference to the original HTTP request object. + * @type {IncomingMessage} + */ + get request(): exports.IncomingMessage; + /** + * A reference to the original HTTP request object. + * @type {IncomingMessage} + */ + get req(): exports.IncomingMessage; + set statusCode(statusCode: number); + /** + * The HTTP request status code. + * Only valid for response obtained from `ClientRequest`. + * @type {number} + */ + get statusCode(): number; + set statusMessage(statusMessage: string); + /** + * The HTTP response status message (reason phrase). + * Such as "OK" or "Internal Server Error." + * Only valid for response obtained from `ClientRequest`. + * @type {string?} + */ + get statusMessage(): string; + set status(status: number); + /** + * An alias for `statusCode` + * @type {number} + */ + get status(): number; + set statusText(statusText: string); + /** + * An alias for `statusMessage` + * @type {string?} + */ + get statusText(): string; + set sendDate(value: boolean); + /** + * If `true`, the "Date" header will be automatically generated and sent in + * the response if it is not already present in the headers. + * Defaults to `true`. + * @type {boolean} + */ + get sendDate(): boolean; + /** + * @ignore + */ + writeContinue(): this; + /** + * @ignore + */ + writeEarlyHints(): this; + /** + * @ignore + */ + writeProcessing(): this; + /** + * Writes the response header to the request. + * The `statusCode` is a 3-digit HTTP status code, like 200 or 404. + * The last argument, `headers`, are the response headers. + * Optionally one can give a human-readable `statusMessage` + * as the second argument. + * @param {number|string} statusCode + * @param {string|object|string[]} [statusMessage] + * @param {object|string[]} [headers] + * @return {ClientRequest} + */ + writeHead(statusCode: number | string, statusMessage?: string | object | string[], headers?: object | string[]): ClientRequest; + #private; + } + /** + * An options object container for an `Agent` instance. + */ + export class AgentOptions { + /** + * `AgentOptions` class constructor. + * @ignore + * @param {{ + * keepAlive?: boolean, + * timeout?: number + * }} [options] + */ + constructor(options?: { + keepAlive?: boolean; + timeout?: number; + }); + keepAlive: boolean; + timeout: number; + } + /** + * An Agent is responsible for managing connection persistence + * and reuse for HTTP clients. + * @see {@link https://nodejs.org/api/http.html#class-httpagent} + */ + export class Agent extends EventEmitter { + /** + * `Agent` class constructor. + * @param {AgentOptions=} [options] + */ + constructor(options?: AgentOptions | undefined); + defaultProtocol: string; + options: any; + requests: Set; + sockets: {}; + maxFreeSockets: number; + maxTotalSockets: number; + maxSockets: number; + /** + * @ignore + */ + get freeSockets(): {}; + /** + * @ignore + * @param {object} options + */ + getName(options: object): string; + /** + * Produces a socket/stream to be used for HTTP requests. + * @param {object} options + * @param {function(Duplex)=} [callback] + * @return {Duplex} + */ + createConnection(options: object, callback?: ((arg0: Duplex) => any) | undefined): Duplex; + /** + * @ignore + */ + keepSocketAlive(): void; + /** + * @ignore + */ + reuseSocket(): void; + /** + * @ignore + */ + destroy(): void; + } + /** + * The global and default HTTP agent. + * @type {Agent} + */ + export const globalAgent: Agent; + /** + * A duplex stream between a HTTP request `IncomingMessage` and the + * response `ServerResponse` + */ + export class Connection extends Duplex { + /** + * `Connection` class constructor. + * @ignore + * @param {Server} server + * @param {IncomingMessage} incomingMessage + * @param {ServerResponse} serverResponse + */ + constructor(server: Server, incomingMessage: IncomingMessage, serverResponse: ServerResponse); + server: any; + active: boolean; + request: any; + response: any; + /** + * Closes the connection, destroying the underlying duplex, request, and + * response streams. + * @return {Connection} + */ + close(): Connection; + } + /** + * A nodejs compat HTTP server typically intended for running in a "worker" + * environment. + * @see {@link https://nodejs.org/api/http.html#class-httpserver} + */ + export class Server extends EventEmitter { + requestTimeout: number; + timeout: number; + maxRequestsPerSocket: number; + keepAliveTimeout: number; + headersTimeout: number; + /** + * @ignore + * @type {AsyncResource} + */ + get resource(): AsyncResource; + /** + * The adapter interface for this `Server` instance. + * @ignore + */ + get adapterInterace(): { + Connection: typeof exports.Connection; + globalAgent: exports.Agent; + IncomingMessage: typeof exports.IncomingMessage; + METHODS: string[]; + ServerResponse: typeof exports.ServerResponse; + STATUS_CODES: any; + }; + /** + * `true` if the server is closed, otherwise `false`. + * @type {boolean} + */ + get closed(): boolean; + /** + * The host to listen to. This value can be `null`. + * Defaults to `location.hostname`. This value + * is used to filter requests by hostname. + * @type {string?} + */ + get host(): string; + /** + * The `port` to listen on. This value can be `0`, which is the default. + * This value is used to filter requests by port, if given. A port value + * of `0` does not filter on any port. + * @type {number} + */ + get port(): number; + /** + * A readonly array of all active or inactive (idle) connections. + * @type {Connection[]} + */ + get connections(): exports.Connection[]; + /** + * `true` if the server is listening for requests. + * @type {boolean} + */ + get listening(): boolean; + set maxConnections(value: number); + /** + * The number of concurrent max connections this server should handle. + * Default: Infinity + * @type {number} + */ + get maxConnections(): number; + /** + * Gets the HTTP server address and port that it this server is + * listening (emulated) on in the runtime with respect to the + * adapter internal being used by the server. + * @return {{ family: string, address: string, port: number}} + */ + address(): { + family: string; + address: string; + port: number; + }; + /** + * Closes the server. + * @param {function=} [close] + */ + close(callback?: any): void; + /** + * Closes all connections. + */ + closeAllConnections(): void; + /** + * Closes all idle connections. + */ + closeIdleConnections(): void; + /** + * @ignore + */ + setTimeout(timeout?: number, callback?: any): this; + /** + * @param {number|object=} [port] + * @param {string=} [host] + * @param {function|null} [unused] + * @param {function=} [callback + * @return Server + */ + listen(port?: (number | object) | undefined, host?: string | undefined, unused?: Function | null, callback?: Function | undefined): this; + #private; + } + export default exports; + import { Writable } from "stream"; + import { Buffer } from "buffer"; + import { Readable } from "stream"; + import { EventEmitter } from "events"; + import { Duplex } from "stream"; + import { AsyncResource } from "async/resource"; + import * as exports from "http"; + namespace ___home_werle_repos_socketsupply_socket_api_http_ { } +} +declare module "https" { + /** + * Makes a HTTPS request, optionally a `socket://` for relative paths when + * `socket:` is the origin protocol. + * @param {string|object} optionsOrURL + * @param {(object|function)=} [options] + * @param {function=} [callback] + * @return {ClientRequest} + */ + export function request(optionsOrURL: string | object, options?: (object | Function) | undefined, callback?: Function | undefined): ClientRequest; + /** + * Makes a HTTPS or `socket:` GET request. A simplified alias to `request()`. + * @param {string|object} optionsOrURL + * @param {(object|function)=} [options] + * @param {function=} [callback] + * @return {ClientRequest} + */ + export function get(optionsOrURL: string | object, options?: (object | Function) | undefined, callback?: Function | undefined): ClientRequest; + /** + * Creates a HTTPS server that can listen for incoming requests. + * Requests that are dispatched to this server depend on the context + * in which it is created, such as a service worker which will use a + * "fetch event" adapter. + * @param {object|function=} [options] + * @param {function=} [callback] + * @return {Server} + */ + export function createServer(...args: any[]): Server; + export const CONTINUE: 100; + export const SWITCHING_PROTOCOLS: 101; + export const PROCESSING: 102; + export const EARLY_HINTS: 103; + export const OK: 200; + export const CREATED: 201; + export const ACCEPTED: 202; + export const NONAUTHORITATIVE_INFORMATION: 203; + export const NO_CONTENT: 204; + export const RESET_CONTENT: 205; + export const PARTIAL_CONTENT: 206; + export const MULTISTATUS: 207; + export const ALREADY_REPORTED: 208; + export const IM_USED: 226; + export const MULTIPLE_CHOICES: 300; + export const MOVED_PERMANENTLY: 301; + export const FOUND: 302; + export const SEE_OTHER: 303; + export const NOT_MODIFIED: 304; + export const USE_PROXY: 305; + export const TEMPORARY_REDIRECT: 307; + export const PERMANENT_REDIRECT: 308; + export const BAD_REQUEST: 400; + export const UNAUTHORIZED: 401; + export const PAYMENT_REQUIRED: 402; + export const FORBIDDEN: 403; + export const NOT_FOUND: 404; + export const METHOD_NOT_ALLOWED: 405; + export const NOT_ACCEPTABLE: 406; + export const PROXY_AUTHENTICATION_REQUIRED: 407; + export const REQUEST_TIMEOUT: 408; + export const CONFLICT: 409; + export const GONE: 410; + export const LENGTH_REQUIRED: 411; + export const PRECONDITION_FAILED: 412; + export const PAYLOAD_TOO_LARGE: 413; + export const URI_TOO_LONG: 414; + export const UNSUPPORTED_MEDIA_TYPE: 415; + export const RANGE_NOT_SATISFIABLE: 416; + export const EXPECTATION_FAILED: 417; + export const IM_A_TEAPOT: 418; + export const MISDIRECTED_REQUEST: 421; + export const UNPROCESSABLE_ENTITY: 422; + export const LOCKED: 423; + export const FAILED_DEPENDENCY: 424; + export const TOO_EARLY: 425; + export const UPGRADE_REQUIRED: 426; + export const PRECONDITION_REQUIRED: 428; + export const TOO_MANY_REQUESTS: 429; + export const REQUEST_HEADER_FIELDS_TOO_LARGE: 431; + export const UNAVAILABLE_FOR_LEGAL_REASONS: 451; + export const INTERNAL_SERVER_ERROR: 500; + export const NOT_IMPLEMENTED: 501; + export const BAD_GATEWAY: 502; + export const SERVICE_UNAVAILABLE: 503; + export const GATEWAY_TIMEOUT: 504; + export const HTTP_VERSION_NOT_SUPPORTED: 505; + export const VARIANT_ALSO_NEGOTIATES: 506; + export const INSUFFICIENT_STORAGE: 507; + export const LOOP_DETECTED: 508; + export const BANDWIDTH_LIMIT_EXCEEDED: 509; + export const NOT_EXTENDED: 510; + export const NETWORK_AUTHENTICATION_REQUIRED: 511; + /** + * All known possible HTTP methods. + * @type {string[]} + */ + export const METHODS: string[]; + /** + * A mapping of status codes to status texts + * @type {object} + */ + export const STATUS_CODES: object; + /** + * An options object container for an `Agent` instance. + */ + export class AgentOptions extends http.AgentOptions { + } + /** + * An Agent is responsible for managing connection persistence + * and reuse for HTTPS clients. + * @see {@link https://nodejs.org/api/https.html#class-httpsagent} + */ + export class Agent extends http.Agent { + } + /** + * An object that is created internally and returned from `request()`. + * @see {@link https://nodejs.org/api/http.html#class-httpclientrequest} + */ + export class ClientRequest extends http.ClientRequest { + } + /** + * The parent class of `ClientRequest` and `ServerResponse`. + * It is an abstract outgoing message from the perspective of the + * participants of an HTTP transaction. + * @see {@link https://nodejs.org/api/http.html#class-httpoutgoingmessage} + */ + export class OutgoingMessage extends http.OutgoingMessage { + } + /** + * An `IncomingMessage` object is created by `Server` or `ClientRequest` and + * passed as the first argument to the 'request' and 'response' event + * respectively. + * It may be used to access response status, headers, and data. + * @see {@link https://nodejs.org/api/http.html#class-httpincomingmessage} + */ + export class IncomingMessage extends http.IncomingMessage { + } + /** + * An object that is created internally by a `Server` instance, not by the user. + * It is passed as the second parameter to the 'request' event. + * @see {@link https://nodejs.org/api/http.html#class-httpserverresponse} + */ + export class ServerResponse extends http.ServerResponse { + } + /** + * A duplex stream between a HTTP request `IncomingMessage` and the + * response `ServerResponse` + */ + export class Connection extends http.Connection { + } + /** + * A nodejs compat HTTP server typically intended for running in a "worker" + * environment. + * @see {@link https://nodejs.org/api/http.html#class-httpserver} + */ + export class Server extends http.Server { + } + /** + * The global and default HTTPS agent. + * @type {Agent} + */ + export const globalAgent: Agent; + export default exports; + import http from "http"; + import * as exports from "http"; +} +declare module "enumeration" { + /** + * @module enumeration + * This module provides a data structure for enumerated unique values. + */ + /** + * A container for enumerated values. + */ + export class Enumeration extends Set { + /** + * Creates an `Enumeration` instance from arguments. + * @param {...any} values + * @return {Enumeration} + */ + static from(...values: any[]): Enumeration; + /** + * `Enumeration` class constructor. + * @param {any[]} values + * @param {object=} [options = {}] + * @param {number=} [options.start = 0] + */ + constructor(values: any[], options?: object | undefined); + /** + * @type {number} + */ + get length(): number; + /** + * Returns `true` if enumeration contains `value`. An alias + * for `Set.prototype.has`. + * @return {boolean} + */ + contains(value: any): boolean; + /** + * @ignore + */ + add(): void; + /** + * @ignore + */ + delete(): void; + /** + * JSON represenation of a `Enumeration` instance. + * @ignore + * @return {string[]} + */ + toJSON(): string[]; + /** + * Internal inspect function. + * @ignore + * @return {LanguageQueryResult} + */ + inspect(): LanguageQueryResult; + } + export default Enumeration; +} +declare module "language" { + /** + * Look up a language name or code by query. + * @param {string} query + * @param {object=} [options] + * @param {boolean=} [options.strict = false] + * @return {?LanguageQueryResult[]} + */ + export function lookup(query: string, options?: object | undefined, ...args: any[]): LanguageQueryResult[] | null; + /** + * Describe a language by tag + * @param {string} query + * @param {object=} [options] + * @param {boolean=} [options.strict = true] + * @return {?LanguageDescription[]} + */ + export function describe(query: string, options?: object | undefined): LanguageDescription[] | null; + /** + * A list of ISO 639-1 language names. + * @type {string[]} + */ + export const names: string[]; + /** + * A list of ISO 639-1 language codes. + * @type {string[]} + */ + export const codes: string[]; + /** + * A list of RFC 5646 language tag identifiers. + * @see {@link http://tools.ietf.org/html/rfc5646} + */ + export const tags: Enumeration; + /** + * A list of RFC 5646 language tag titles corresponding + * to language tags. + * @see {@link http://tools.ietf.org/html/rfc5646} + */ + export const descriptions: Enumeration; + /** + * A container for a language query response containing an ISO language + * name and code. + * @see {@link https://www.sitepoint.com/iso-2-letter-language-codes} + */ + export class LanguageQueryResult { + /** + * `LanguageQueryResult` class constructor. + * @param {string} code + * @param {string} name + * @param {string[]} [tags] + */ + constructor(code: string, name: string, tags?: string[]); + /** + * The language code corresponding to the query. + * @type {string} + */ + get code(): string; + /** + * The language name corresponding to the query. + * @type {string} + */ + get name(): string; + /** + * The language tags corresponding to the query. + * @type {string[]} + */ + get tags(): string[]; + /** + * JSON represenation of a `LanguageQueryResult` instance. + * @return {{ + * code: string, + * name: string, + * tags: string[] + * }} + */ + toJSON(): { + code: string; + name: string; + tags: string[]; + }; + /** + * Internal inspect function. + * @ignore + * @return {LanguageQueryResult} + */ + inspect(): LanguageQueryResult; + #private; + } + /** + * A container for a language code, tag, and description. + */ + export class LanguageDescription { + /** + * `LanguageDescription` class constructor. + * @param {string} code + * @param {string} tag + * @param {string} description + */ + constructor(code: string, tag: string, description: string); + /** + * The language code corresponding to the language + * @type {string} + */ + get code(): string; + /** + * The language tag corresponding to the language. + * @type {string} + */ + get tag(): string; + /** + * The language description corresponding to the language. + * @type {string} + */ + get description(): string; + /** + * JSON represenation of a `LanguageDescription` instance. + * @return {{ + * code: string, + * tag: string, + * description: string + * }} + */ + toJSON(): { + code: string; + tag: string; + description: string; + }; + /** + * Internal inspect function. + * @ignore + * @return {LanguageDescription} + */ + inspect(): LanguageDescription; + #private; + } + namespace _default { + export { codes }; + export { describe }; + export { lookup }; + export { names }; + export { tags }; + } + export default _default; + import Enumeration from "enumeration"; +} +declare module "latica/packets" { + /** + * The magic bytes prefixing every packet. They are the + * 2nd, 3rd, 5th, and 7th, prime numbers. + * @type {number[]} + */ + export const MAGIC_BYTES_PREFIX: number[]; + /** + * The version of the protocol. + */ + export const VERSION: 6; + /** + * The size in bytes of the prefix magic bytes. + */ + export const MAGIC_BYTES: 4; + /** + * The maximum size of the user message. + */ + export const MESSAGE_BYTES: 1024; + /** + * The cache TTL in milliseconds. + */ + export const CACHE_TTL: number; + export namespace PACKET_SPEC { + namespace type { + let bytes: number; + let encoding: string; + } + namespace version { + let bytes_1: number; + export { bytes_1 as bytes }; + let encoding_1: string; + export { encoding_1 as encoding }; + export { VERSION as default }; + } + namespace clock { + let bytes_2: number; + export { bytes_2 as bytes }; + let encoding_2: string; + export { encoding_2 as encoding }; + let _default: number; + export { _default as default }; + } + namespace hops { + let bytes_3: number; + export { bytes_3 as bytes }; + let encoding_3: string; + export { encoding_3 as encoding }; + let _default_1: number; + export { _default_1 as default }; + } + namespace index { + let bytes_4: number; + export { bytes_4 as bytes }; + let encoding_4: string; + export { encoding_4 as encoding }; + let _default_2: number; + export { _default_2 as default }; + export let signed: boolean; + } + namespace ttl { + let bytes_5: number; + export { bytes_5 as bytes }; + let encoding_5: string; + export { encoding_5 as encoding }; + export { CACHE_TTL as default }; + } + namespace clusterId { + let bytes_6: number; + export { bytes_6 as bytes }; + let encoding_6: string; + export { encoding_6 as encoding }; + let _default_3: number[]; + export { _default_3 as default }; + } + namespace subclusterId { + let bytes_7: number; + export { bytes_7 as bytes }; + let encoding_7: string; + export { encoding_7 as encoding }; + let _default_4: number[]; + export { _default_4 as default }; + } + namespace previousId { + let bytes_8: number; + export { bytes_8 as bytes }; + let encoding_8: string; + export { encoding_8 as encoding }; + let _default_5: number[]; + export { _default_5 as default }; + } + namespace packetId { + let bytes_9: number; + export { bytes_9 as bytes }; + let encoding_9: string; + export { encoding_9 as encoding }; + let _default_6: number[]; + export { _default_6 as default }; + } + namespace nextId { + let bytes_10: number; + export { bytes_10 as bytes }; + let encoding_10: string; + export { encoding_10 as encoding }; + let _default_7: number[]; + export { _default_7 as default }; + } + namespace usr1 { + let bytes_11: number; + export { bytes_11 as bytes }; + let _default_8: number[]; + export { _default_8 as default }; + } + namespace usr2 { + let bytes_12: number; + export { bytes_12 as bytes }; + let _default_9: number[]; + export { _default_9 as default }; + } + namespace usr3 { + let bytes_13: number; + export { bytes_13 as bytes }; + let _default_10: number[]; + export { _default_10 as default }; + } + namespace usr4 { + let bytes_14: number; + export { bytes_14 as bytes }; + let _default_11: number[]; + export { _default_11 as default }; + } + namespace message { + let bytes_15: number; + export { bytes_15 as bytes }; + let _default_12: number[]; + export { _default_12 as default }; + } + namespace sig { + let bytes_16: number; + export { bytes_16 as bytes }; + let _default_13: number[]; + export { _default_13 as default }; + } + } + /** + * The size in bytes of the total packet frame and message. + */ + export const PACKET_BYTES: number; + /** + * The maximum distance that a packet can be replicated. + */ + export const MAX_HOPS: 16; + export function validateMessage(o: object, constraints: { + [key: string]: constraint; + }): void; + /** + * Computes a SHA-256 hash of input returning a hex encoded string. + * @type {function(string|Buffer|Uint8Array): Promise} + */ + export const sha256: (arg0: string | Buffer | Uint8Array) => Promise; + export function decode(buf: Buffer): Packet; + export function getTypeFromBytes(buf: any): any; + export class Packet { + static ttl: number; + static maxLength: number; + /** + * Returns an empty `Packet` instance. + * @return {Packet} + */ + static empty(): Packet; + /** + * @param {Packet|object} packet + * @return {Packet} + */ + static from(packet: Packet | object): Packet; + /** + * Determines if input is a packet. + * @param {Buffer|Uint8Array|number[]|object|Packet} packet + * @return {boolean} + */ + static isPacket(packet: Buffer | Uint8Array | number[] | object | Packet): boolean; + /** + */ + static encode(p: any): Promise; + static decode(buf: any): Packet; + /** + * `Packet` class constructor. + * @param {Packet|object?} options + */ + constructor(options?: Packet | (object | null)); + /** + * @param {Packet} packet + * @return {Packet} + */ + copy(): Packet; + timestamp: any; + isComposed: any; + isReconciled: any; + meta: any; + } + export class PacketPing extends Packet { + static type: number; + } + export class PacketPong extends Packet { + static type: number; + } + export class PacketIntro extends Packet { + static type: number; + } + export class PacketJoin extends Packet { + static type: number; + } + export class PacketPublish extends Packet { + static type: number; + } + export class PacketStream extends Packet { + static type: number; + } + export class PacketSync extends Packet { + static type: number; + } + export class PacketQuery extends Packet { + static type: number; + } + export default Packet; + export type constraint = { + type: string; + required?: boolean; + /** + * optional validator fn returning boolean + */ + assert?: Function; + }; + import { Buffer } from "buffer"; +} +declare module "latica/encryption" { + /** + * Class for handling encryption and key management. + */ + export class Encryption { + /** + * Creates a shared key based on the provided seed or generates a random one. + * @param {Uint8Array|string} seed - Seed for key generation. + * @returns {Promise} - Shared key. + */ + static createSharedKey(seed: Uint8Array | string): Promise; + /** + * Creates a key pair for signing and verification. + * @param {Uint8Array|string} seed - Seed for key generation. + * @returns {Promise<{ publicKey: Uint8Array, privateKey: Uint8Array }>} - Key pair. + */ + static createKeyPair(seed: Uint8Array | string): Promise<{ + publicKey: Uint8Array; + privateKey: Uint8Array; + }>; + /** + * Creates an ID using SHA-256 hash. + * @param {string} str - String to hash. + * @returns {Promise} - SHA-256 hash. + */ + static createId(str: string): Promise; + /** + * Creates a cluster ID using SHA-256 hash with specified output size. + * @param {string} str - String to hash. + * @returns {Promise} - SHA-256 hash with specified output size. + */ + static createClusterId(str: string): Promise; + /** + * Signs a message using the given secret key. + * @param {Buffer} b - The message to sign. + * @param {Uint8Array} sk - The secret key to use. + * @returns {Uint8Array} - Signature. + */ + static sign(b: Buffer, sk: Uint8Array): Uint8Array; + /** + * Verifies the signature of a message using the given public key. + * @param {Buffer} b - The message to verify. + * @param {Uint8Array} sig - The signature to check. + * @param {Uint8Array} pk - The public key to use. + * @returns {number} - Returns non-zero if the buffer could not be verified. + */ + static verify(b: Buffer, sig: Uint8Array, pk: Uint8Array): number; + /** + * Mapping of public keys to key objects. + * @type {Object.} + */ + keys: { + [x: string]: { + publicKey: Uint8Array; + privateKey: Uint8Array; + ts: number; + }; + }; + /** + * Adds a key pair to the keys mapping. + * @param {Uint8Array|string} publicKey - Public key. + * @param {Uint8Array} privateKey - Private key. + */ + add(publicKey: Uint8Array | string, privateKey: Uint8Array): void; + /** + * Removes a key from the keys mapping. + * @param {Uint8Array|string} publicKey - Public key. + */ + remove(publicKey: Uint8Array | string): void; + /** + * Checks if a key is in the keys mapping. + * @param {Uint8Array|string} to - Public key or Uint8Array. + * @returns {boolean} - True if the key is present, false otherwise. + */ + has(to: Uint8Array | string): boolean; + /** + * Opens a sealed message using the specified key. + * @param {Buffer} message - The sealed message. + * @param {Object|string} v - Key object or public key. + * @returns {Buffer} - Decrypted message. + * @throws {Error} - Throws ENOKEY if the key is not found. + */ + openUnsigned(message: Buffer, v: any | string): Buffer; + sealUnsigned(message: any, v: any): any; + /** + * Decrypts a sealed and signed message for a specific receiver. + * @param {Buffer} message - The sealed message. + * @param {Object|string} v - Key object or public key. + * @returns {Buffer} - Decrypted message. + * @throws {Error} - Throws ENOKEY if the key is not found, EMALFORMED if the message is malformed, ENOTVERIFIED if the message cannot be verified. + */ + open(message: Buffer, v: any | string): Buffer; + /** + * Seals and signs a message for a specific receiver using their public key. + * + * `Seal(message, receiver)` performs an _encrypt-sign-encrypt_ (ESE) on + * a plaintext `message` for a `receiver` identity. This prevents repudiation + * attacks and doesn't rely on packet chain guarantees. + * + * let ct = Seal(sender | pt, receiver) + * let sig = Sign(ct, sk) + * let out = Seal(sig | ct) + * + * In an setup between Alice & Bob, this means: + * - Only Bob sees the plaintext + * - Alice wrote the plaintext and the ciphertext + * - Only Bob can see that Alice wrote the plaintext and ciphertext + * - Bob cannot forward the message without invalidating Alice's signature. + * - The outer encryption serves to prevent an attacker from replacing Alice's + * signature. As with _sign-encrypt-sign (SES), ESE is a variant of + * including the recipient's name inside the plaintext, which is then signed + * and encrypted Alice signs her plaintext along with her ciphertext, so as + * to protect herself from a laintext-substitution attack. At the same time, + * Alice's signed plaintext gives Bob non-repudiation. + * + * @see https://theworld.com/~dtd/sign_encrypt/sign_encrypt7.html + * + * @param {Buffer} message - The message to seal. + * @param {Object|string} v - Key object or public key. + * @returns {Buffer} - Sealed message. + * @throws {Error} - Throws ENOKEY if the key is not found. + */ + seal(message: Buffer, v: any | string): Buffer; + } + import Buffer from "buffer"; +} +declare module "latica/cache" { + /** + * @typedef {Packet} CacheEntry + * @typedef {function(CacheEntry, CacheEntry): number} CacheEntrySiblingResolver + */ + /** + * Default cache sibling resolver that computes a delta between + * two entries clocks. + * @param {CacheEntry} a + * @param {CacheEntry} b + * @return {number} + */ + export function defaultSiblingResolver(a: CacheEntry, b: CacheEntry): number; + /** + * Default max size of a `Cache` instance. + */ + export const DEFAULT_MAX_SIZE: number; + /** + * Internal mapping of packet IDs to packet data used by `Cache`. + */ + export class CacheData extends Map { + constructor(); + constructor(entries?: readonly (readonly [any, any])[]); + constructor(); + constructor(iterable?: Iterable); + } + /** + * A class for storing a cache of packets by ID. This class includes a scheme + * for reconciling disjointed packet caches in a large distributed system. The + * following are key design characteristics. + * + * Space Efficiency: This scheme can be space-efficient because it summarizes + * the cache's contents in a compact binary format. By sharing these summaries, + * two computers can quickly determine whether their caches have common data or + * differences. + * + * Bandwidth Efficiency: Sharing summaries instead of the full data can save + * bandwidth. If the differences between the caches are small, sharing summaries + * allows for more efficient data synchronization. + * + * Time Efficiency: The time efficiency of this scheme depends on the size of + * the cache and the differences between the two caches. Generating summaries + * and comparing them can be faster than transferring and comparing the entire + * dataset, especially for large caches. + * + * Complexity: The scheme introduces some complexity due to the need to encode + * and decode summaries. In some cases, the overhead introduced by this + * complexity might outweigh the benefits, especially if the caches are + * relatively small. In this case, you should be using a query. + * + * Data Synchronization Needs: The efficiency also depends on the data + * synchronization needs. If the data needs to be synchronized in real-time, + * this scheme might not be suitable. It's more appropriate for cases where + * periodic or batch synchronization is acceptable. + * + * Scalability: The scheme's efficiency can vary depending on the scalability + * of the system. As the number of cache entries or computers involved + * increases, the complexity of generating and comparing summaries will stay + * bound to a maximum of 16Mb. + * + */ + export class Cache { + static HASH_SIZE_BYTES: number; + static HASH_EMPTY: string; + /** + * The encodeSummary method provides a compact binary encoding of the output + * of summary() + * + * @param {Object} summary - the output of calling summary() + * @return {Buffer} + **/ + static encodeSummary(summary: any): Buffer; + /** + * The decodeSummary method decodes the output of encodeSummary() + * + * @param {Buffer} bin - the output of calling encodeSummary() + * @return {Object} summary + **/ + static decodeSummary(bin: Buffer): any; + /** + * Test a summary hash format is valid + * + * @param {string} hash + * @returns boolean + */ + static isValidSummaryHashFormat(hash: string): boolean; + /** + * `Cache` class constructor. + * @param {CacheData?} [data] + */ + constructor(data?: CacheData | null, siblingResolver?: typeof defaultSiblingResolver); + data: CacheData; + maxSize: number; + siblingResolver: typeof defaultSiblingResolver; + /** + * Readonly count of the number of cache entries. + * @type {number} + */ + get size(): number; + /** + * Readonly size of the cache in bytes. + * @type {number} + */ + get bytes(): number; + /** + * Inserts a `CacheEntry` value `v` into the cache at key `k`. + * @param {string} k + * @param {CacheEntry} v + * @return {boolean} + */ + insert(k: string, v: CacheEntry): boolean; + /** + * Gets a `CacheEntry` value at key `k`. + * @param {string} k + * @return {CacheEntry?} + */ + get(k: string): CacheEntry | null; + /** + * @param {string} k + * @return {boolean} + */ + delete(k: string): boolean; + /** + * Predicate to determine if cache contains an entry at key `k`. + * @param {string} k + * @return {boolean} + */ + has(k: string): boolean; + /** + * Composes an indexed packet into a new `Packet` + * @param {Packet} packet + */ + compose(packet: Packet, source?: CacheData): Promise; + sha1(value: any, toHex: any): Promise; + /** + * + * The summarize method returns a terse yet comparable summary of the cache + * contents. + * + * Think of the cache as a trie of hex characters, the summary returns a + * checksum for the current level of the trie and for its 16 children. + * + * This is similar to a merkel tree as equal subtrees can easily be detected + * without the need for further recursion. When the subtree checksums are + * inequivalent then further negotiation at lower levels may be required, this + * process continues until the two trees become synchonized. + * + * When the prefix is empty, the summary will return an array of 16 checksums + * these checksums provide a way of comparing that subtree with other peers. + * + * When a variable-length hexidecimal prefix is provided, then only cache + * member hashes sharing this prefix will be considered. + * + * For each hex character provided in the prefix, the trie will decend by one + * level, each level divides the 2^128 address space by 16. For exmaple... + * + * ``` + * Level 0 1 2 + * ---------------- + * 2b00 + * aa0e ━┓ ━┓ + * aa1b ┃ ┃ + * aae3 ┃ ┃ ━┓ + * aaea ┃ ┃ ┃ + * aaeb ┃ ━┛ ━┛ + * ab00 ┃ ━┓ + * ab1e ┃ ┃ + * ab2a ┃ ┃ + * abef ┃ ┃ + * abf0 ━┛ ━┛ + * bff9 + * ``` + * + * @param {string} prefix - a string of lowercased hexidecimal characters + * @return {Object} + * + */ + summarize(prefix?: string, predicate?: (o: any) => boolean): any; + } + export default Cache; + export type CacheEntry = Packet; + export type CacheEntrySiblingResolver = (arg0: CacheEntry, arg1: CacheEntry) => number; + import { Packet } from "latica/packets"; + import { Buffer } from "buffer"; +} +declare module "latica/nat" { + /** + * The NAT type is encoded using 5 bits: + * + * 0b00001 : the lsb indicates if endpoint dependence information is included + * 0b00010 : the second bit indicates the endpoint dependence value + * + * 0b00100 : the third bit indicates if firewall information is included + * 0b01000 : the fourth bit describes which requests can pass the firewall, only known IPs (0) or any IP (1) + * 0b10000 : the fifth bit describes which requests can pass the firewall, only known ports (0) or any port (1) + */ + /** + * Every remote will see the same IP:PORT mapping for this peer. + * + * :3333 ┌──────┐ + * :1111 ┌───▶ │ R1 │ + * ┌──────┐ ┌───────┐ │ └──────┘ + * │ P1 ├───▶│ NAT ├──┤ + * └──────┘ └───────┘ │ ┌──────┐ + * └───▶ │ R2 │ + * :3333 └──────┘ + */ + export const MAPPING_ENDPOINT_INDEPENDENT: 3; + /** + * Every remote will see a different IP:PORT mapping for this peer. + * + * :4444 ┌──────┐ + * :1111 ┌───▶ │ R1 │ + * ┌──────┐ ┌───────┐ │ └──────┘ + * │ P1 ├───▶│ NAT ├──┤ + * └──────┘ └───────┘ │ ┌──────┐ + * └───▶ │ R2 │ + * :5555 └──────┘ + */ + export const MAPPING_ENDPOINT_DEPENDENT: 1; + /** + * The firewall allows the port mapping to be accessed by: + * - Any IP:PORT combination (FIREWALL_ALLOW_ANY) + * - Any PORT on a previously connected IP (FIREWALL_ALLOW_KNOWN_IP) + * - Only from previously connected IP:PORT combinations (FIREWALL_ALLOW_KNOWN_IP_AND_PORT) + */ + export const FIREWALL_ALLOW_ANY: 28; + export const FIREWALL_ALLOW_KNOWN_IP: 12; + export const FIREWALL_ALLOW_KNOWN_IP_AND_PORT: 4; + /** + * The initial state of the nat is unknown and its value is 0 + */ + export const UNKNOWN: 0; + /** + * Full-cone NAT, also known as one-to-one NAT + * + * Any external host can send packets to iAddr:iPort by sending packets to eAddr:ePort. + * + * @summary its a packet party at this mapping and everyone's invited + */ + export const UNRESTRICTED: number; + /** + * (Address)-restricted-cone NAT + * + * An external host (hAddr:any) can send packets to iAddr:iPort by sending packets to eAddr:ePort only + * if iAddr:iPort has previously sent a packet to hAddr:any. "Any" means the port number doesn't matter. + * + * @summary The NAT will drop your packets unless a peer within its network has previously messaged you from *any* port. + */ + export const ADDR_RESTRICTED: number; + /** + * Port-restricted cone NAT + * + * An external host (hAddr:hPort) can send packets to iAddr:iPort by sending + * packets to eAddr:ePort only if iAddr:iPort has previously sent a packet to + * hAddr:hPort. + * + * @summary The NAT will drop your packets unless a peer within its network + * has previously messaged you from this *specific* port. + */ + export const PORT_RESTRICTED: number; + /** + * Symmetric NAT + * + * Only an external host that receives a packet from an internal host can send + * a packet back. + * + * @summary The NAT will only accept replies to a correspondence initialized + * by itself, the mapping it created is only valid for you. + */ + export const ENDPOINT_RESTRICTED: number; + export function isEndpointDependenceDefined(nat: any): boolean; + export function isFirewallDefined(nat: any): boolean; + export function isValid(nat: any): boolean; + export function toString(n: any): "UNRESTRICTED" | "ADDR_RESTRICTED" | "PORT_RESTRICTED" | "ENDPOINT_RESTRICTED" | "UNKNOWN"; + export function toStringStrategy(n: any): "STRATEGY_DEFER" | "STRATEGY_DIRECT_CONNECT" | "STRATEGY_TRAVERSAL_OPEN" | "STRATEGY_TRAVERSAL_CONNECT" | "STRATEGY_PROXY" | "STRATEGY_UNKNOWN"; + export const STRATEGY_DEFER: 0; + export const STRATEGY_DIRECT_CONNECT: 1; + export const STRATEGY_TRAVERSAL_OPEN: 2; + export const STRATEGY_TRAVERSAL_CONNECT: 3; + export const STRATEGY_PROXY: 4; + export function connectionStrategy(a: any, b: any): 0 | 1 | 2 | 3 | 4; +} +declare module "latica/index" { + /** + * Computes rate limit predicate value for a port and address pair for a given + * threshold updating an input rates map. This method is accessed concurrently, + * the rates object makes operations atomic to avoid race conditions. + * + * @param {Map} rates + * @param {number} type + * @param {number} port + * @param {string} address + * @return {boolean} + */ + export function rateLimit(rates: Map, type: number, port: number, address: string, subclusterIdQuota: any): boolean; + /** + * Retry delay in milliseconds for ping. + * @type {number} + */ + export const PING_RETRY: number; + /** + * Probe wait timeout in milliseconds. + * @type {number} + */ + export const PROBE_WAIT: number; + /** + * Default keep alive timeout. + * @type {number} + */ + export const DEFAULT_KEEP_ALIVE: number; + /** + * Default rate limit threshold in milliseconds. + * @type {number} + */ + export const DEFAULT_RATE_LIMIT_THRESHOLD: number; + export function getRandomPort(ports: object, p: number | null): number; + /** + * A `RemotePeer` represents an initial, discovered, or connected remote peer. + * Typically, you will not need to create instances of this class directly. + */ + export class RemotePeer { + /** + * `RemotePeer` class constructor. + * @param {{ + * peerId?: string, + * address?: string, + * port?: number, + * natType?: number, + * clusters: object, + * reflectionId?: string, + * distance?: number, + * publicKey?: string, + * privateKey?: string, + * clock?: number, + * lastUpdate?: number, + * lastRequest?: number + * }} o + */ + constructor(o: { + peerId?: string; + address?: string; + port?: number; + natType?: number; + clusters: object; + reflectionId?: string; + distance?: number; + publicKey?: string; + privateKey?: string; + clock?: number; + lastUpdate?: number; + lastRequest?: number; + }, peer: any); + peerId: any; + address: any; + port: number; + natType: any; + clusters: {}; + pingId: any; + distance: number; + connected: boolean; + opening: number; + probed: number; + proxy: any; + clock: number; + uptime: number; + lastUpdate: number; + lastRequest: number; + localPeer: any; + write(sharedKey: any, args: any): Promise; + } + /** + * `Peer` class factory. + * @param {{ createSocket: function('udp4', null, object?): object }} options + */ + export class Peer { + /** + * Test a peerID is valid + * + * @param {string} pid + * @returns boolean + */ + static isValidPeerId(pid: string): boolean; + /** + * Test a reflectionID is valid + * + * @param {string} rid + * @returns boolean + */ + static isValidReflectionId(rid: string): boolean; + /** + * Test a pingID is valid + * + * @param {string} pid + * @returns boolean + */ + static isValidPingId(pid: string): boolean; + /** + * Returns the online status of the browser, else true. + * + * note: globalThis.navigator was added to node in v22. + * + * @returns boolean + */ + static onLine(): boolean; + /** + * `Peer` class constructor. + * @param {object=} opts - Options + * @param {Buffer} opts.peerId - A 32 byte buffer (ie, `Encryption.createId()`). + * @param {Buffer} opts.clusterId - A 32 byte buffer (ie, `Encryption.createClusterId()`). + * @param {number=} opts.port - A port number. + * @param {number=} opts.probeInternalPort - An internal port number (semi-private for testing). + * @param {number=} opts.probeExternalPort - An external port number (semi-private for testing). + * @param {number=} opts.natType - A nat type. + * @param {string=} opts.address - An ipv4 address. + * @param {number=} opts.keepalive - The interval of the main loop. + * @param {function=} opts.siblingResolver - A function that can be used to determine canonical data in case two packets have concurrent clock values. + * @param {object} dgram - A nodejs compatible implementation of the dgram module (sans multicast). + */ + constructor(persistedState: {}, dgram: object); + port: any; + address: any; + natType: number; + nextNatType: number; + clusters: {}; + syncs: {}; + reflectionId: any; + reflectionTimeout: any; + reflectionStage: number; + reflectionRetry: number; + reflectionFirstResponder: any; + peerId: string; + isListening: boolean; + ctime: number; + lastUpdate: number; + lastSync: number; + closing: boolean; + clock: number; + unpublished: {}; + cache: any; + uptime: number; + maxHops: number; + bdpCache: number[]; + dgram: any; + onListening: any; + onDelete: any; + sendQueue: any[]; + firewall: any; + rates: Map; + streamBuffer: Map; + gate: Map; + returnRoutes: Map; + metrics: { + i: { + 0: number; + 1: number; + 2: number; + 3: number; + 4: number; + 5: number; + 6: number; + 7: number; + 8: number; + DROPPED: number; + }; + o: { + 0: number; + 1: number; + 2: number; + 3: number; + 4: number; + 5: number; + 6: number; + 7: number; + 8: number; + }; + }; + peers: any; + encryption: Encryption; + config: any; + _onError: (err: any) => any; + socket: any; + probeSocket: any; + /** + * An implementation for clearning an interval that can be overridden by the test suite + * @param Number the number that identifies the timer + * @return {undefined} + * @ignore + */ + _clearInterval(tid: any): undefined; + /** + * An implementation for clearning a timeout that can be overridden by the test suite + * @param Number the number that identifies the timer + * @return {undefined} + * @ignore + */ + _clearTimeout(tid: any): undefined; + /** + * An implementation of an internal timer that can be overridden by the test suite + * @return {Number} + * @ignore + */ + _setInterval(fn: any, t: any): number; + /** + * An implementation of an timeout timer that can be overridden by the test suite + * @return {Number} + * @ignore + */ + _setTimeout(fn: any, t: any): number; + _onDebug(...args: any[]): void; + /** + * A method that encapsulates the listing procedure + * @return {undefined} + * @ignore + */ + _listen(): undefined; + init(cb: any): Promise; + onReady: any; + mainLoopTimer: number; + /** + * Continuously evaluate the state of the peer and its network + * @return {undefined} + * @ignore + */ + _mainLoop(ts: any): undefined; + /** + * Enqueue packets to be sent to the network + * @param {Buffer} data - An encoded packet + * @param {number} port - The desination port of the remote host + * @param {string} address - The destination address of the remote host + * @param {Socket=this.socket} socket - The socket to send on + * @return {undefined} + * @ignore + */ + send(data: Buffer, port: number, address: string, socket?: any): undefined; + /** + * @private + */ + private stream; + /** + * @private + */ + private _scheduleSend; + sendTimeout: number; + /** + * @private + */ + private _dequeue; + /** + * Send any unpublished packets + * @return {undefined} + * @ignore + */ + sendUnpublished(): undefined; + /** + * Get the serializable state of the peer (can be passed to the constructor or create method) + * @return {undefined} + */ + getState(): undefined; + getInfo(): Promise<{ + address: any; + port: any; + clock: number; + uptime: number; + natType: number; + natName: string; + peerId: string; + }>; + cacheInsert(packet: any): Promise; + addIndexedPeer(info: any): Promise; + reconnect(): Promise; + disconnect(): Promise; + probeReflectionTimeout: any; + sealUnsigned(...args: any[]): Promise; + openUnsigned(...args: any[]): Promise; + seal(...args: any[]): Promise; + open(...args: any[]): Promise; + addEncryptionKey(...args: any[]): Promise; + /** + * Get a selection of known peers + * @return {Array} + * @ignore + */ + getPeers(packet: any, peers: any, ignorelist: any, filter?: (o: any) => any): Array; + /** + * Send an eventually consistent packet to a selection of peers (fanout) + * @return {undefined} + * @ignore + */ + mcast(packet: any, ignorelist?: any[]): undefined; + /** + * The process of determining this peer's NAT behavior (firewall and dependentness) + * @return {undefined} + * @ignore + */ + requestReflection(): undefined; + /** + * Ping another peer + * @return {PacketPing} + * @ignore + */ + ping(peer: any, withRetry: any, props: any, socket: any): PacketPing; + /** + * Get a peer + * @return {RemotePeer} + * @ignore + */ + getPeer(id: any): RemotePeer; + /** + * This should be called at least once when an app starts to multicast + * this peer, and starts querying the network to discover peers. + * @param {object} keys - Created by `Encryption.createKeyPair()`. + * @param {object=} args - Options + * @param {number=MAX_BANDWIDTH} args.rateLimit - How many requests per second to allow for this subclusterId. + * @return {RemotePeer} + */ + join(sharedKey: any, args?: object | undefined): RemotePeer; + /** + * @param {Packet} T - The constructor to be used to create packets. + * @param {Any} message - The message to be split and packaged. + * @return {Array>} + * @ignore + */ + _message2packets(T: Packet, message: Any, args: any): Array>; + /** + * Sends a packet into the network that will be replicated and buffered. + * Each peer that receives it will buffer it until TTL and then replicate + * it provided it has has not exceeded their maximum number of allowed hops. + * + * @param {object} keys - the public and private key pair created by `Encryption.createKeyPair()`. + * @param {object} args - The arguments to be applied. + * @param {Buffer} args.message - The message to be encrypted by keys and sent. + * @param {Packet=} args.packet - The previous packet in the packet chain. + * @param {Buffer} args.usr1 - 32 bytes of arbitrary clusterId in the protocol framing. + * @param {Buffer} args.usr2 - 32 bytes of arbitrary clusterId in the protocol framing. + * @return {Array} + */ + publish(sharedKey: any, args: { + message: Buffer; + packet?: Packet | undefined; + usr1: Buffer; + usr2: Buffer; + }): Array; + /** + * @return {undefined} + */ + sync(peer: any, ptime?: number): undefined; + close(): void; + /** + * Deploy a query into the network + * @return {undefined} + * + */ + query(query: any): undefined; + /** + * + * This is a default implementation for deciding what to summarize + * from the cache when receiving a request to sync. that can be overridden + * + */ + cachePredicate(ts: any): (packet: any) => boolean; + /** + * A connection was made, add the peer to the local list of known + * peers and call the onConnection if it is defined by the user. + * + * @return {undefined} + * @ignore + */ + _onConnection(packet: any, peerId: any, port: any, address: any, proxy: any, socket: any): undefined; + /** + * Received a Sync Packet + * @return {undefined} + * @ignore + */ + _onSync(packet: any, port: any, address: any): undefined; + /** + * Received a Query Packet + * + * a -> b -> c -> (d) -> c -> b -> a + * + * @return {undefined} + * @example + * + * ```js + * peer.onQuery = (packet) => { + * // + * // read a database or something + * // + * return { + * message: Buffer.from('hello'), + * publicKey: '', + * privateKey: '' + * } + * } + * ``` + */ + _onQuery(packet: any, port: any, address: any): undefined; + /** + * Received a Ping Packet + * @return {undefined} + * @ignore + */ + _onPing(packet: any, port: any, address: any): undefined; + /** + * Received a Pong Packet + * @return {undefined} + * @ignore + */ + _onPong(packet: any, port: any, address: any): undefined; + reflectionFirstReponderTimeout: number; + /** + * Received an Intro Packet + * @return {undefined} + * @ignore + */ + _onIntro(packet: any, port: any, address: any, _: any, opts?: { + attempts: number; + }): undefined; + socketPool: any[]; + /** + * Received an Join Packet + * @return {undefined} + * @ignore + */ + _onJoin(packet: any, port: any, address: any, data: any): undefined; + /** + * Received an Publish Packet + * @return {undefined} + * @ignore + */ + _onPublish(packet: any, port: any, address: any, data: any): undefined; + /** + * Received an Stream Packet + * @return {undefined} + * @ignore + */ + _onStream(packet: any, port: any, address: any, data: any): undefined; + /** + * Received any packet on the probe port to determine the firewall: + * are you port restricted, host restricted, or unrestricted. + * @return {undefined} + * @ignore + */ + _onProbeMessage(data: any, { port, address }: { + port: any; + address: any; + }): undefined; + /** + * When a packet is received it is decoded, the packet contains the type + * of the message. Based on the message type it is routed to a function. + * like WebSockets, don't answer queries unless we know its another SRP peer. + * + * @param {Buffer|Uint8Array} data + * @param {{ port: number, address: string }} info + */ + _onMessage(data: Buffer | Uint8Array, { port, address }: { + port: number; + address: string; + }): Promise; + } + export default Peer; + import { Packet } from "latica/packets"; + import { sha256 } from "latica/packets"; + import { Cache } from "latica/cache"; + import { Encryption } from "latica/encryption"; + import * as NAT from "latica/nat"; + import { Buffer } from "buffer"; + import { PacketPing } from "latica/packets"; + import { PacketPublish } from "latica/packets"; + export { Packet, sha256, Cache, Encryption, NAT }; +} +declare module "latica/proxy" { + export default PeerWorkerProxy; + /** + * `Proxy` class factory, returns a Proxy class that is a proxy to the Peer. + * @param {{ createSocket: function('udp4', null, object?): object }} options + */ + export class PeerWorkerProxy { + constructor(options: any, port: any, fn: any); + init(): Promise; + reconnect(): Promise; + disconnect(): Promise; + getInfo(): Promise; + getMetrics(): Promise; + getState(): Promise; + open(...args: any[]): Promise; + seal(...args: any[]): Promise; + sealUnsigned(...args: any[]): Promise; + openUnsigned(...args: any[]): Promise; + addEncryptionKey(...args: any[]): Promise; + send(...args: any[]): Promise; + sendUnpublished(...args: any[]): Promise; + cacheInsert(...args: any[]): Promise; + mcast(...args: any[]): Promise; + requestReflection(...args: any[]): Promise; + stream(...args: any[]): Promise; + join(...args: any[]): Promise; + publish(...args: any[]): Promise; + sync(...args: any[]): Promise; + close(...args: any[]): Promise; + query(...args: any[]): Promise; + compileCachePredicate(src: any): Promise; + callWorkerThread(prop: any, data: any): any; + callMainThread(prop: any, args: any): void; + resolveMainThread(seq: any, result: any): any; + #private; + } +} +declare module "latica/api" { + export default api; + /** + * Initializes and returns the network bus. + * + * @async + * @function + * @param {object} options - Configuration options for the network bus. + * @param {object} events - A nodejs compatibe implementation of the events module. + * @param {object} dgram - A nodejs compatible implementation of the dgram module. + * @returns {Promise} - A promise that resolves to the initialized network bus. + */ + export function api(options: object, events: object, dgram: object): Promise; +} +declare module "network" { + export default network; + export function network(options: any): Promise; + import { Cache } from "latica/index"; + import { sha256 } from "latica/index"; + import { Encryption } from "latica/index"; + import { Packet } from "latica/index"; + import { NAT } from "latica/index"; + export { Cache, sha256, Encryption, Packet, NAT }; +} +declare module "service-worker" { + /** + * A reference to the opened environment. This value is an instance of an + * `Environment` if the scope is a ServiceWorker scope. + * @type {Environment|null} + */ + export const env: Environment | null; + namespace _default { + export { ExtendableEvent }; + export { FetchEvent }; + export { Environment }; + export { Context }; + export { env }; + } + export default _default; + import { Environment } from "service-worker/env"; + import { ExtendableEvent } from "service-worker/events"; + import { FetchEvent } from "service-worker/events"; + import { Context } from "service-worker/context"; + export { ExtendableEvent, FetchEvent, Environment, Context }; +} +declare module "string_decoder" { + export function StringDecoder(encoding: any): void; + export class StringDecoder { + constructor(encoding: any); + encoding: any; + text: typeof utf16Text | typeof base64Text; + end: typeof utf16End | typeof base64End | typeof simpleEnd; + fillLast: typeof utf8FillLast; + write: typeof simpleWrite; + lastNeed: number; + lastTotal: number; + lastChar: Uint8Array; + } + export default StringDecoder; + function utf16Text(buf: any, i: any): any; + class utf16Text { + constructor(buf: any, i: any); + lastNeed: number; + lastTotal: number; + } + function base64Text(buf: any, i: any): any; + class base64Text { + constructor(buf: any, i: any); + lastNeed: number; + lastTotal: number; + } + function utf16End(buf: any): any; + function base64End(buf: any): any; + function simpleEnd(buf: any): any; + function utf8FillLast(buf: any): any; + function simpleWrite(buf: any): any; +} +declare module "test/context" { + export default function _default(GLOBAL_TEST_RUNNER: any): void; +} +declare module "test/dom-helpers" { + /** + * Converts querySelector string to an HTMLElement or validates an existing HTMLElement. + * + * @export + * @param {string|Element} selector - A CSS selector string, or an instance of HTMLElement, or Element. + * @returns {Element} The HTMLElement, Element, or Window that corresponds to the selector. + * @throws {Error} Throws an error if the `selector` is not a string that resolves to an HTMLElement or not an instance of HTMLElement, Element, or Window. + * + */ + export function toElement(selector: string | Element): Element; + /** + * Waits for an element to appear in the DOM and resolves the promise when it does. + * + * @export + * @param {Object} args - Configuration arguments. + * @param {string} [args.selector] - The CSS selector to look for. + * @param {boolean} [args.visible=true] - Whether the element should be visible. + * @param {number} [args.timeout=defaultTimeout] - Time in milliseconds to wait before rejecting the promise. + * @param {() => HTMLElement | Element | null | undefined} [lambda] - An optional function that returns the element. Used if the `selector` is not provided. + * @returns {Promise} - A promise that resolves to the found element. + * + * @throws {Error} - Throws an error if neither `lambda` nor `selector` is provided. + * @throws {Error} - Throws an error if the element is not found within the timeout. + * + * @example + * ```js + * waitFor({ selector: '#my-element', visible: true, timeout: 5000 }) + * .then(el => console.log('Element found:', el)) + * .catch(err => console.log('Element not found:', err)); + * ``` + */ + export function waitFor(args: { + selector?: string; + visible?: boolean; + timeout?: number; + }, lambda?: () => HTMLElement | Element | null | undefined): Promise; + /** + * Waits for an element's text content to match a given string or regular expression. + * + * @export + * @param {Object} args - Configuration arguments. + * @param {Element} args.element - The root element from which to begin searching. + * @param {string} [args.text] - The text to search for within elements. + * @param {RegExp} [args.regex] - A regular expression to match against element text content. + * @param {boolean} [args.multipleTags=false] - Whether to look for text across multiple sibling elements. + * @param {number} [args.timeout=defaultTimeout] - Time in milliseconds to wait before rejecting the promise. + * @returns {Promise} - A promise that resolves to the found element or null. + * + * @example + * ```js + * waitForText({ element: document.body, text: 'Hello', timeout: 5000 }) + * .then(el => console.log('Element found:', el)) + * .catch(err => console.log('Element not found:', err)); + * ``` + */ + export function waitForText(args: { + element: Element; + text?: string; + regex?: RegExp; + multipleTags?: boolean; + timeout?: number; + }): Promise; + /** + * @export + * @param {Object} args - Arguments + * @param {string | Event} args.event - The event to dispatch. + * @param {HTMLElement | Element | window} [args.element=window] - The element to dispatch the event on. + * @returns {void} + * + * @throws {Error} Throws an error if the `event` is not a string that can be converted to a CustomEvent or not an instance of Event. + */ + export function event(args: { + event: string | Event; + element?: HTMLElement | Element | (Window & typeof globalThis); + }): void; + /** + * @export + * Copy pasted from https://raw.githubusercontent.com/testing-library/jest-dom/master/src/to-be-visible.js + * @param {Element | HTMLElement} element + * @param {Element | HTMLElement} [previousElement] + * @returns {boolean} + */ + export function isElementVisible(element: Element | HTMLElement, previousElement?: Element | HTMLElement): boolean; +} +declare module "test/index" { + /** + * @returns {number} - The default timeout for tests in milliseconds. + */ + export function getDefaultTestRunnerTimeout(): number; + /** + * @param {string} name + * @param {TestFn} [fn] + * @returns {void} + */ + export function only(name: string, fn?: TestFn): void; + /** + * @param {string} _name + * @param {TestFn} [_fn] + * @returns {void} + */ + export function skip(_name: string, _fn?: TestFn): void; + /** + * @param {boolean} strict + * @returns {void} + */ + export function setStrict(strict: boolean): void; + /** + * @typedef {{ + * (name: string, fn?: TestFn): void + * only(name: string, fn?: TestFn): void + * skip(name: string, fn?: TestFn): void + * }} testWithProperties + * @ignore + */ + /** + * @type {testWithProperties} + * @param {string} name + * @param {TestFn} [fn] + * @returns {void} + */ + export function test(name: string, fn?: TestFn): void; + export namespace test { + export { only }; + export { skip }; + export function linux(name: any, fn: any): void; + export function windows(name: any, fn: any): void; + export function win32(name: any, fn: any): void; + export function unix(name: any, fn: any): void; + export function macosx(name: any, fn: any): void; + export function macos(name: any, fn: any): void; + export function mac(name: any, fn: any): void; + export function darwin(name: any, fn: any): void; + export function iphone(name: any, fn: any): void; + export namespace iphone { + function simulator(name: any, fn: any): void; + } + export function ios(name: any, fn: any): void; + export namespace ios { + function simulator(name: any, fn: any): void; + } + export function android(name: any, fn: any): void; + export namespace android { + function emulator(name: any, fn: any): void; + } + export function desktop(name: any, fn: any): void; + export function mobile(name: any, fn: any): void; + } + /** + * @typedef {(t: Test) => (void | Promise)} TestFn + */ + /** + * @class + */ + export class Test { + /** + * @constructor + * @param {string} name + * @param {TestFn} fn + * @param {TestRunner} runner + */ + constructor(name: string, fn: TestFn, runner: TestRunner); + /** + * @type {string} + * @ignore + */ + name: string; + /** + * @type {null|number} + * @ignore + */ + _planned: null | number; + /** + * @type {null|number} + * @ignore + */ + _actual: null | number; + /** + * @type {TestFn} + * @ignore + */ + fn: TestFn; + /** + * @type {TestRunner} + * @ignore + */ + runner: TestRunner; + /** + * @type{{ pass: number, fail: number }} + * @ignore + */ + _result: { + pass: number; + fail: number; + }; + /** + * @type {boolean} + * @ignore + */ + done: boolean; + /** + * @type {boolean} + * @ignore + */ + strict: boolean; + /** + * @param {string} msg + * @returns {void} + */ + comment(msg: string): void; + /** + * Plan the number of assertions. + * + * @param {number} n + * @returns {void} + */ + plan(n: number): void; + /** + * @template T + * @param {T} actual + * @param {T} expected + * @param {string} [msg] + * @returns {void} + */ + deepEqual(actual: T, expected: T, msg?: string): void; + /** + * @template T + * @param {T} actual + * @param {T} expected + * @param {string} [msg] + * @returns {void} + */ + notDeepEqual(actual: T, expected: T, msg?: string): void; + /** + * @template T + * @param {T} actual + * @param {T} expected + * @param {string} [msg] + * @returns {void} + */ + equal(actual: T, expected: T, msg?: string): void; + /** + * @param {unknown} actual + * @param {unknown} expected + * @param {string} [msg] + * @returns {void} + */ + notEqual(actual: unknown, expected: unknown, msg?: string): void; + /** + * @param {string} [msg] + * @returns {void} + */ + fail(msg?: string): void; + /** + * @param {unknown} actual + * @param {string} [msg] + * @returns {void} + */ + ok(actual: unknown, msg?: string): void; + /** + * @param {string} [msg] + * @returns {void} + */ + pass(msg?: string): void; + /** + * @param {Error | null | undefined} err + * @param {string} [msg] + * @returns {void} + */ + ifError(err: Error | null | undefined, msg?: string): void; + /** + * @param {Function} fn + * @param {RegExp | any} [expected] + * @param {string} [message] + * @returns {void} + */ + throws(fn: Function, expected?: RegExp | any, message?: string): void; + /** + * Sleep for ms with an optional msg + * + * @param {number} ms + * @param {string} [msg] + * @returns {Promise} + * + * @example + * ```js + * await t.sleep(100) + * ``` + */ + sleep(ms: number, msg?: string): Promise; + /** + * Request animation frame with an optional msg. Falls back to a 0ms setTimeout when + * tests are run headlessly. + * + * @param {string} [msg] + * @returns {Promise} + * + * @example + * ```js + * await t.requestAnimationFrame() + * ``` + */ + requestAnimationFrame(msg?: string): Promise; + /** + * Dispatch the `click`` method on an element specified by selector. + * + * @param {string|HTMLElement|Element} selector - A CSS selector string, or an instance of HTMLElement, or Element. + * @param {string} [msg] + * @returns {Promise} + * + * @example + * ```js + * await t.click('.class button', 'Click a button') + * ``` + */ + click(selector: string | HTMLElement | Element, msg?: string): Promise; + /** + * Dispatch the click window.MouseEvent on an element specified by selector. + * + * @param {string|HTMLElement|Element} selector - A CSS selector string, or an instance of HTMLElement, or Element. + * @param {string} [msg] + * @returns {Promise} + * + * @example + * ```js + * await t.eventClick('.class button', 'Click a button with an event') + * ``` + */ + eventClick(selector: string | HTMLElement | Element, msg?: string): Promise; + /** + * Dispatch an event on the target. + * + * @param {string | Event} event - The event name or Event instance to dispatch. + * @param {string|HTMLElement|Element} target - A CSS selector string, or an instance of HTMLElement, or Element to dispatch the event on. + * @param {string} [msg] + * @returns {Promise} + * + * @example + * ```js + * await t.dispatchEvent('my-event', '#my-div', 'Fire the my-event event') + * ``` + */ + dispatchEvent(event: string | Event, target: string | HTMLElement | Element, msg?: string): Promise; + /** + * Call the focus method on element specified by selector. + * + * @param {string|HTMLElement|Element} selector - A CSS selector string, or an instance of HTMLElement, or Element. + * @param {string} [msg] + * @returns {Promise} + * + * @example + * ```js + * await t.focus('#my-div') + * ``` + */ + focus(selector: string | HTMLElement | Element, msg?: string): Promise; + /** + * Call the blur method on element specified by selector. + * + * @param {string|HTMLElement|Element} selector - A CSS selector string, or an instance of HTMLElement, or Element. + * @param {string} [msg] + * @returns {Promise} + * + * @example + * ```js + * await t.blur('#my-div') + * ``` + */ + blur(selector: string | HTMLElement | Element, msg?: string): Promise; + /** + * Consecutively set the str value of the element specified by selector to simulate typing. + * + * @param {string|HTMLElement|Element} selector - A CSS selector string, or an instance of HTMLElement, or Element. + * @param {string} str - The string to type into the :focus element. + * @param {string} [msg] + * @returns {Promise} + * + * @example + * ```js + * await t.typeValue('#my-div', 'Hello World', 'Type "Hello World" into #my-div') + * ``` + */ + type(selector: string | HTMLElement | Element, str: string, msg?: string): Promise; + /** + * appendChild an element el to a parent selector element. + * + * @param {string|HTMLElement|Element} parentSelector - A CSS selector string, or an instance of HTMLElement, or Element to appendChild on. + * @param {HTMLElement|Element} el - A element to append to the parent element. + * @param {string} [msg] + * @returns {Promise} + * + * @example + * ```js + * const myElement = createElement('div') + * await t.appendChild('#parent-selector', myElement, 'Append myElement into #parent-selector') + * ``` + */ + appendChild(parentSelector: string | HTMLElement | Element, el: HTMLElement | Element, msg?: string): Promise; + /** + * Remove an element from the DOM. + * + * @param {string|HTMLElement|Element} selector - A CSS selector string, or an instance of HTMLElement, or Element to remove from the DOM. + * @param {string} [msg] + * @returns {Promise} + * + * @example + * ```js + * await t.removeElement('#dom-selector', 'Remove #dom-selector') + * ``` + */ + removeElement(selector: string | HTMLElement | Element, msg?: string): Promise; + /** + * Test if an element is visible + * + * @param {string|HTMLElement|Element} selector - A CSS selector string, or an instance of HTMLElement, or Element to test visibility on. + * @param {string} [msg] + * @returns {Promise} + * + * @example + * ```js + * await t.elementVisible('#dom-selector','Element is visible') + * ``` + */ + elementVisible(selector: string | HTMLElement | Element, msg?: string): Promise; + /** + * Test if an element is invisible + * + * @param {string|HTMLElement|Element} selector - A CSS selector string, or an instance of HTMLElement, or Element to test visibility on. + * @param {string} [msg] + * @returns {Promise} + * + * @example + * ```js + * await t.elementInvisible('#dom-selector','Element is invisible') + * ``` + */ + elementInvisible(selector: string | HTMLElement | Element, msg?: string): Promise; + /** + * Test if an element is invisible + * + * @param {string|(() => HTMLElement|Element|null|undefined)} querySelectorOrFn - A query string or a function that returns an element. + * @param {Object} [opts] + * @param {boolean} [opts.visible] - The element needs to be visible. + * @param {number} [opts.timeout] - The maximum amount of time to wait. + * @param {string} [msg] + * @returns {Promise} + * + * @example + * ```js + * await t.waitFor('#dom-selector', { visible: true },'#dom-selector is on the page and visible') + * ``` + */ + waitFor(querySelectorOrFn: string | (() => HTMLElement | Element | null | undefined), opts?: { + visible?: boolean; + timeout?: number; + }, msg?: string): Promise; + /** + * @typedef {Object} WaitForTextOpts + * @property {string} [text] - The text to wait for + * @property {number} [timeout] + * @property {Boolean} [multipleTags] + * @property {RegExp} [regex] The regex to wait for + */ + /** + * Test if an element is invisible + * + * @param {string|HTMLElement|Element} selector - A CSS selector string, or an instance of HTMLElement, or Element. + * @param {WaitForTextOpts | string | RegExp} [opts] + * @param {string} [msg] + * @returns {Promise} + * + * @example + * ```js + * await t.waitForText('#dom-selector', 'Text to wait for') + * ``` + * + * @example + * ```js + * await t.waitForText('#dom-selector', /hello/i) + * ``` + * + * @example + * ```js + * await t.waitForText('#dom-selector', { + * text: 'Text to wait for', + * multipleTags: true + * }) + * ``` + */ + waitForText(selector: string | HTMLElement | Element, opts?: { + /** + * - The text to wait for + */ + text?: string; + timeout?: number; + multipleTags?: boolean; + /** + * The regex to wait for + */ + regex?: RegExp; + } | string | RegExp, msg?: string): Promise; + /** + * Run a querySelector as an assert and also get the results + * + * @param {string} selector - A CSS selector string, or an instance of HTMLElement, or Element to select. + * @param {string} [msg] + * @returns {HTMLElement | Element} + * + * @example + * ```js + * const element = await t.querySelector('#dom-selector') + * ``` + */ + querySelector(selector: string, msg?: string): HTMLElement | Element; + /** + * Run a querySelectorAll as an assert and also get the results + * + * @param {string} selector - A CSS selector string, or an instance of HTMLElement, or Element to select. + * @param {string} [msg] + @returns {Array} + * + * @example + * ```js + * const elements = await t.querySelectorAll('#dom-selector', '') + * ``` + */ + querySelectorAll(selector: string, msg?: string): Array; + /** + * Retrieves the computed styles for a given element. + * + * @param {string|Element} selector - The CSS selector or the Element object for which to get the computed styles. + * @param {string} [msg] - An optional message to display when the operation is successful. Default message will be generated based on the type of selector. + * @returns {CSSStyleDeclaration} - The computed styles of the element. + * @throws {Error} - Throws an error if the element has no `ownerDocument` or if `ownerDocument.defaultView` is not available. + * + * @example + * ```js + * // Using CSS selector + * const style = getComputedStyle('.my-element', 'Custom success message'); + * ``` + * + * @example + * ```js + * // Using Element object + * const el = document.querySelector('.my-element'); + * const style = getComputedStyle(el); + * ``` + */ + getComputedStyle(selector: string | Element, msg?: string): CSSStyleDeclaration; + /** + * @param {boolean} pass + * @param {unknown} actual + * @param {unknown} expected + * @param {string} description + * @param {string} operator + * @returns {void} + * @ignore + */ + _assert(pass: boolean, actual: unknown, expected: unknown, description: string, operator: string): void; + /** + * @returns {Promise<{ + * pass: number, + * fail: number + * }>} + */ + run(): Promise<{ + pass: number; + fail: number; + }>; + } + /** + * @class + */ + export class TestRunner { + /** + * @constructor + * @param {(lines: string) => void} [report] + */ + constructor(report?: (lines: string) => void); + /** + * @type {(lines: string) => void} + * @ignore + */ + report: (lines: string) => void; + /** + * @type {Test[]} + * @ignore + */ + tests: Test[]; + /** + * @type {Test[]} + * @ignore + */ + onlyTests: Test[]; + /** + * @type {boolean} + * @ignore + */ + scheduled: boolean; + /** + * @type {number} + * @ignore + */ + _id: number; + /** + * @type {boolean} + * @ignore + */ + completed: boolean; + /** + * @type {boolean} + * @ignore + */ + rethrowExceptions: boolean; + /** + * @type {boolean} + * @ignore + */ + strict: boolean; + /** + * @type {function | void} + * @ignore + */ + _onFinishCallback: Function | void; + /** + * @returns {string} + */ + nextId(): string; + /** + * @type {number} + */ + get length(): number; + /** + * @param {string} name + * @param {TestFn} fn + * @param {boolean} only + * @returns {void} + */ + add(name: string, fn: TestFn, only: boolean): void; + /** + * @returns {Promise} + */ + run(): Promise; + /** + * @param {(result: { total: number, success: number, fail: number }) => void} callback + * @returns {void} + */ + onFinish(callback: (result: { + total: number; + success: number; + fail: number; + }) => void): void; + } + /** + * @ignore + */ + export const GLOBAL_TEST_RUNNER: TestRunner; + export default test; + export type testWithProperties = { + (name: string, fn?: TestFn): void; + only(name: string, fn?: TestFn): void; + skip(name: string, fn?: TestFn): void; + }; + export type TestFn = (t: Test) => (void | Promise); +} +declare module "test" { + export * from "test/index"; + export default test; + import test from "test/index"; +} +declare module "commonjs/builtins" { + /** + * Defines a builtin module by name making a shallow copy of the + * module exports. + * @param {string} + * @param {object} exports + */ + export function defineBuiltin(name: any, exports: object, copy?: boolean): void; + /** + * Predicate to determine if a given module name is a builtin module. + * @param {string} name + * @param {{ builtins?: object }} + * @return {boolean} + */ + export function isBuiltin(name: string, options?: any): boolean; + /** + * Gets a builtin module by name. + * @param {string} name + * @param {{ builtins?: object }} [options] + * @return {any} + */ + export function getBuiltin(name: string, options?: { + builtins?: object; + }): any; + /** + * A mapping of builtin modules + * @type {object} + */ + export const builtins: object; + /** + * Known runtime specific builtin modules. + * @type {Set} + */ + export const runtimeModules: Set; + export default builtins; +} +declare module "commonjs/cache" { + /** + * @typedef {{ + * types?: object, + * loader?: import('./loader.js').Loader + * }} CacheOptions + */ + export const CACHE_CHANNEL_MESSAGE_ID: "id"; + export const CACHE_CHANNEL_MESSAGE_REPLICATE: "replicate"; + /** + * @typedef {{ + * name: string + * }} StorageOptions + */ + /** + * An storage context object with persistence and durability + * for service worker storages. + */ + export class Storage extends EventTarget { + /** + * Maximum entries that will be restored from storage into the context object. + * @type {number} + */ + static MAX_CONTEXT_ENTRIES: number; + /** + * A mapping of known `Storage` instances. + * @type {Map} + */ + static instances: Map; + /** + * Opens an storage for a particular name. + * @param {StorageOptions} options + * @return {Promise} + */ + static open(options: StorageOptions): Promise; + /** + * `Storage` class constructor + * @ignore + * @param {StorageOptions} options + */ + constructor(options: StorageOptions); + /** + * A reference to the currently opened storage database. + * @type {import('../internal/database.js').Database} + */ + get database(): import("internal/database").Database; + /** + * `true` if the storage is opened, otherwise `false`. + * @type {boolean} + */ + get opened(): boolean; + /** + * `true` if the storage is opening, otherwise `false`. + * @type {boolean} + */ + get opening(): boolean; + /** + * A proxied object for reading and writing storage state. + * Values written to this object must be cloneable with respect to the + * structured clone algorithm. + * @see {https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm} + * @type {Proxy} + */ + get context(): ProxyConstructor; + /** + * The current storage name. This value is also used as the + * internal database name. + * @type {string} + */ + get name(): string; + /** + * A promise that resolves when the storage is opened. + * @type {Promise?} + */ + get ready(): Promise; + /** + * @ignore + * @param {Promise} promise + */ + forwardRequest(promise: Promise): Promise; + /** + * Resets the current storage to an empty state. + */ + reset(): Promise; + /** + * Synchronizes database entries into the storage context. + */ + sync(options?: any): Promise; + /** + * Opens the storage. + * @ignore + */ + open(options?: any): Promise; + /** + * Closes the storage database, purging existing state. + * @ignore + */ + close(): Promise; + #private; + } + /** + * A container for `Snapshot` data storage. + */ + export class SnapshotData { + /** + * `SnapshotData` class constructor. + * @param {object=} [data] + */ + constructor(data?: object | undefined); + toJSON: () => this; + [Symbol.toStringTag]: string; + } + /** + * A container for storing a snapshot of the cache data. + */ + export class Snapshot { + /** + * @type {typeof SnapshotData} + */ + static Data: typeof SnapshotData; + /** + * A reference to the snapshot data. + * @type {Snapshot.Data} + */ + get data(): typeof SnapshotData; + /** + * @ignore + * @return {object} + */ + toJSON(): object; + #private; + } + /** + * An interface for managing and performing operations on a collection + * of `Cache` objects. + */ + export class CacheCollection { + /** + * `CacheCollection` class constructor. + * @ignore + * @param {Cache[]|Record=} [collection] + */ + constructor(collection?: (Cache[] | Record) | undefined); + /** + * Adds a `Cache` instance to the collection. + * @param {string|Cache} name + * @param {Cache=} [cache] + * @param {boolean} + */ + add(name: string | Cache, cache?: Cache | undefined): any; + /** + * Calls a method on each `Cache` object in the collection. + * @param {string} method + * @param {...any} args + * @return {Promise>} + */ + call(method: string, ...args: any[]): Promise>; + restore(): Promise>; + reset(): Promise>; + snapshot(): Promise>; + get(key: any): Promise>; + delete(key: any): Promise>; + keys(key: any): Promise>; + values(key: any): Promise>; + clear(key: any): Promise>; + } + /** + * A container for a shared cache that lives for the life time of + * application execution. Updates to this storage are replicated to other + * instances in the application context, including windows and workers. + */ + export class Cache { + /** + * A globally shared type mapping for the cache to use when + * derserializing a value. + * @type {Map} + */ + static types: Map; + /** + * A globally shared cache store keyed by cache name. This is useful so + * when multiple instances of a `Cache` are created, they can share the + * same data store, reducing duplications. + * @type {Record} + */ + static shared: Record>; + /** + * A mapping of opened `Storage` instances. + * @type {Map} + */ + static storages: Map; + /** + * The `Cache.Snapshot` class. + * @type {typeof Snapshot} + */ + static Snapshot: typeof Snapshot; + /** + * The `Cache.Storage` class + * @type {typeof Storage} + */ + static Storage: typeof Storage; + /** + * Creates a snapshot of the current cache which can be serialized and + * stored in persistent storage. + * @return {Snapshot} + */ + static snapshot(): Snapshot; + /** + * Restore caches from persistent storage. + * @param {string[]} names + * @return {Promise} + */ + static restore(names: string[]): Promise; + /** + * `Cache` class constructor. + * @param {string} name + * @param {CacheOptions=} [options] + */ + constructor(name: string, options?: CacheOptions | undefined); + /** + * The unique ID for this cache. + * @type {string} + */ + get id(): string; + /** + * The loader associated with this cache. + * @type {import('./loader.js').Loader} + */ + get loader(): import("commonjs/loader").Loader; + /** + * A reference to the persisted storage. + * @type {Storage} + */ + get storage(): Storage; + /** + * The cache name + * @type {string} + */ + get name(): string; + /** + * The underlying cache data map. + * @type {Map} + */ + get data(): Map; + /** + * The broadcast channel associated with this cach. + * @type {BroadcastChannel} + */ + get channel(): BroadcastChannel; + /** + * The size of the cache. + * @type {number} + */ + get size(): number; + /** + * @type {Map} + */ + get types(): Map; + /** + * Resets the cache map and persisted storage. + */ + reset(): Promise; + /** + * Restores cache data from storage. + */ + restore(): Promise; + /** + * Creates a snapshot of the current cache which can be serialized and + * stored in persistent storage. + * @return {Snapshot.Data} + */ + snapshot(): typeof SnapshotData; + /** + * Get a value at `key`. + * @param {string} key + * @return {object|undefined} + */ + get(key: string): object | undefined; + /** + * Set `value` at `key`. + * @param {string} key + * @param {object} value + * @return {Cache} + */ + set(key: string, value: object): Cache; + /** + * Returns `true` if `key` is in cache, otherwise `false`. + * @param {string} + * @return {boolean} + */ + has(key: any): boolean; + /** + * Delete a value at `key`. + * This does not replicate to shared caches. + * @param {string} key + * @return {boolean} + */ + delete(key: string): boolean; + /** + * Returns an iterator for all cache keys. + * @return {object} + */ + keys(): object; + /** + * Returns an iterator for all cache values. + * @return {object} + */ + values(): object; + /** + * Returns an iterator for all cache entries. + * @return {object} + */ + entries(): object; + /** + * Clears all entries in the cache. + * This does not replicate to shared caches. + * @return {undefined} + */ + clear(): undefined; + /** + * Enumerates entries in map calling `callback(value, key + * @param {function(object, string, Cache): any} callback + */ + forEach(callback: (arg0: object, arg1: string, arg2: Cache) => any): void; + /** + * Broadcasts a replication to other shared caches. + */ + replicate(): this; + /** + * Destroys the cache. This function stops the broadcast channel and removes + * and listeners + */ + destroy(): void; + /** + * @ignore + */ + [Symbol.iterator](): any; + #private; + } + export default Cache; + export type CacheOptions = { + types?: object; + loader?: import("commonjs/loader").Loader; + }; + export type StorageOptions = { + name: string; + }; +} +declare module "commonjs/loader" { + /** + * @typedef {{ + * extensions?: string[] | Set + * origin?: URL | string, + * statuses?: Cache + * cache?: { response?: Cache, status?: Cache }, + * headers?: Headers | Map | object | string[][] + * }} LoaderOptions + */ + /** + * @typedef {{ + * loader?: Loader, + * origin?: URL | string + * }} RequestOptions + */ + /** + * @typedef {{ + * headers?: Headers | object | array[], + * status?: number + * }} RequestStatusOptions + */ + /** + * @typedef {{ + * headers?: Headers | object + * }} RequestLoadOptions + */ + /** + * @typedef {{ + * request?: Request, + * headers?: Headers, + * status?: number, + * buffer?: ArrayBuffer, + * text?: string + * }} ResponseOptions + */ + /** + * A container for the status of a CommonJS resource. A `RequestStatus` object + * represents meta data for a `Request` that comes from a preflight + * HTTP HEAD request. + */ + export class RequestStatus { + /** + * Creates a `RequestStatus` from JSON input. + * @param {object} json + * @return {RequestStatus} + */ + static from(json: object, options: any): RequestStatus; + /** + * `RequestStatus` class constructor. + * @param {Request} request + * @param {RequestStatusOptions} [options] + */ + constructor(request: Request, options?: RequestStatusOptions); + set request(request: Request); + /** + * The `Request` object associated with this `RequestStatus` object. + * @type {Request} + */ + get request(): Request; + /** + * The unique ID of this `RequestStatus`, which is the absolute URL as a string. + * @type {string} + */ + get id(): string; + /** + * The origin for this `RequestStatus` object. + * @type {string} + */ + get origin(): string; + /** + * A HTTP status code for this `RequestStatus` object. + * @type {number|undefined} + */ + get status(): number; + /** + * An alias for `status`. + * @type {number|undefined} + */ + get value(): number; + /** + * @ignore + */ + get valueOf(): number; + /** + * The HTTP headers for this `RequestStatus` object. + * @type {Headers} + */ + get headers(): Headers; + /** + * The resource location for this `RequestStatus` object. This value is + * determined from the 'Content-Location' header, if available, otherwise + * it is derived from the request URL pathname (including the query string). + * @type {string} + */ + get location(): string; + /** + * `true` if the response status is considered OK, otherwise `false`. + * @type {boolean} + */ + get ok(): boolean; + /** + * Loads the internal state for this `RequestStatus` object. + * @param {RequestLoadOptions|boolean} [options] + * @return {RequestStatus} + */ + load(options?: RequestLoadOptions | boolean): RequestStatus; + /** + * Converts this `RequestStatus` to JSON. + * @ignore + * @return {{ + * id: string, + * origin: string | null, + * status: number, + * headers: Array + * request: object | null | undefined + * }} + */ + toJSON(includeRequest?: boolean): { + id: string; + origin: string | null; + status: number; + headers: Array; + request: object | null | undefined; + }; + #private; + } + /** + * A container for a synchronous CommonJS request to local resource or + * over the network. + */ + export class Request { + /** + * Creates a `Request` instance from JSON input + * @param {object} json + * @param {RequestOptions=} [options] + * @return {Request} + */ + static from(json: object, options?: RequestOptions | undefined): Request; + /** + * `Request` class constructor. + * @param {URL|string} url + * @param {URL|string=} [origin] + * @param {RequestOptions=} [options] + */ + constructor(url: URL | string, origin?: (URL | string) | undefined, options?: RequestOptions | undefined); + /** + * The unique ID of this `Request`, which is the absolute URL as a string. + * @type {string} + */ + get id(): string; + /** + * The absolute `URL` of this `Request` object. + * @type {URL} + */ + get url(): URL; + /** + * The origin for this `Request`. + * @type {string} + */ + get origin(): string; + /** + * The `Loader` for this `Request` object. + * @type {Loader?} + */ + get loader(): Loader; + /** + * The `RequestStatus` for this `Request` + * @type {RequestStatus} + */ + get status(): RequestStatus; + /** + * Loads the CommonJS source file, optionally checking the `Loader` cache + * first, unless ignored when `options.cache` is `false`. + * @param {RequestLoadOptions=} [options] + * @return {Response} + */ + load(options?: RequestLoadOptions | undefined): Response; + /** + * Converts this `Request` to JSON. + * @ignore + * @return {{ + * url: string, + * status: object | undefined + * }} + */ + toJSON(includeStatus?: boolean): { + url: string; + status: object | undefined; + }; + #private; + } + /** + * A container for a synchronous CommonJS request response for a local resource + * or over the network. + */ + export class Response { + /** + * Creates a `Response` from JSON input + * @param {obejct} json + * @param {ResponseOptions=} [options] + * @return {Response} + */ + static from(json: obejct, options?: ResponseOptions | undefined): Response; + /** + * `Response` class constructor. + * @param {Request|ResponseOptions} request + * @param {ResponseOptions=} [options] + */ + constructor(request: Request | ResponseOptions, options?: ResponseOptions | undefined); + /** + * The unique ID of this `Response`, which is the absolute + * URL of the request as a string. + * @type {string} + */ + get id(): string; + /** + * The `Request` object associated with this `Response` object. + * @type {Request} + */ + get request(): Request; + /** + * The response headers from the associated request. + * @type {Headers} + */ + get headers(): Headers; + /** + * The `Loader` associated with this `Response` object. + * @type {Loader?} + */ + get loader(): Loader; + /** + * The `Response` status code from the associated `Request` object. + * @type {number} + */ + get status(): number; + /** + * The `Response` string from the associated `Request` + * @type {string} + */ + get text(): string; + /** + * The `Response` array buffer from the associated `Request` + * @type {ArrayBuffer?} + */ + get buffer(): ArrayBuffer; + /** + * `true` if the response is considered OK, otherwise `false`. + * @type {boolean} + */ + get ok(): boolean; + /** + * Converts this `Response` to JSON. + * @ignore + * @return {{ + * id: string, + * text: string, + * status: number, + * buffer: number[] | null, + * headers: Array + * }} + */ + toJSON(): { + id: string; + text: string; + status: number; + buffer: number[] | null; + headers: Array; + }; + #private; + } + /** + * A container for loading CommonJS module sources + */ + export class Loader { + /** + * A request class used by `Loader` objects. + * @type {typeof Request} + */ + static Request: typeof Request; + /** + * A response class used by `Loader` objects. + * @type {typeof Request} + */ + static Response: typeof Request; + /** + * Resolves a given module URL to an absolute URL with an optional `origin`. + * @param {URL|string} url + * @param {URL|string} [origin] + * @return {string} + */ + static resolve(url: URL | string, origin?: URL | string): string; + /** + * Default extensions for a loader. + * @type {Set} + */ + static defaultExtensions: Set; + /** + * `Loader` class constructor. + * @param {string|URL|LoaderOptions} origin + * @param {LoaderOptions=} [options] + */ + constructor(origin: string | URL | LoaderOptions, options?: LoaderOptions | undefined); + /** + * The internal caches for this `Loader` object. + * @type {{ response: Cache, status: Cache }} + */ + get cache(): { + response: Cache; + status: Cache; + }; + /** + * Headers used in too loader requests. + * @type {Headers} + */ + get headers(): Headers; + /** + * A set of supported `Loader` extensions. + * @type {Set} + */ + get extensions(): Set; + set origin(origin: string); + /** + * The origin of this `Loader` object. + * @type {string} + */ + get origin(): string; + /** + * Loads a CommonJS module source file at `url` with an optional `origin`, which + * defaults to the application origin. + * @param {URL|string} url + * @param {URL|string|object} [origin] + * @param {RequestOptions=} [options] + * @return {Response} + */ + load(url: URL | string, origin?: URL | string | object, options?: RequestOptions | undefined): Response; + /** + * Queries the status of a CommonJS module source file at `url` with an + * optional `origin`, which defaults to the application origin. + * @param {URL|string} url + * @param {URL|string|object} [origin] + * @param {RequestOptions=} [options] + * @return {RequestStatus} + */ + status(url: URL | string, origin?: URL | string | object, options?: RequestOptions | undefined): RequestStatus; + /** + * Resolves a given module URL to an absolute URL based on the loader origin. + * @param {URL|string} url + * @param {URL|string} [origin] + * @return {string} + */ + resolve(url: URL | string, origin?: URL | string): string; + #private; + } + export default Loader; + export type LoaderOptions = { + extensions?: string[] | Set; + origin?: URL | string; + statuses?: Cache; + cache?: { + response?: Cache; + status?: Cache; + }; + headers?: Headers | Map | object | string[][]; + }; + export type RequestOptions = { + loader?: Loader; + origin?: URL | string; + }; + export type RequestStatusOptions = { + headers?: Headers | object | any[][]; + status?: number; + }; + export type RequestLoadOptions = { + headers?: Headers | object; + }; + export type ResponseOptions = { + request?: Request; + headers?: Headers; + status?: number; + buffer?: ArrayBuffer; + text?: string; + }; + import { Headers } from "ipc"; + import URL from "url"; + import { Cache } from "commonjs/cache"; +} +declare module "commonjs/package" { + /** + * @ignore + * @param {string} source + * @return {boolean} + */ + export function detectESMSource(source: string): boolean; + /** + * @typedef {{ + * manifest?: string, + * index?: string, + * description?: string, + * version?: string, + * license?: string, + * exports?: object, + * type?: 'commonjs' | 'module', + * info?: object, + * origin?: string, + * dependencies?: Dependencies | object | Map + * }} PackageOptions + */ + /** + * @typedef {import('./loader.js').RequestOptions & { + * type?: 'commonjs' | 'module' + * prefix?: string + * }} PackageLoadOptions + */ + /** + * {import('./loader.js').RequestOptions & { + * load?: boolean, + * type?: 'commonjs' | 'module', + * browser?: boolean, + * children?: string[] + * extensions?: string[] | Set + * }} PackageResolveOptions + */ + /** + * @typedef {{ + * organization: string | null, + * name: string, + * version: string | null, + * pathname: string, + * url: URL, + * isRelative: boolean, + * hasManifest: boolean + * }} ParsedPackageName + */ + /** + * @typedef {{ + * require?: string | string[], + * import?: string | string[], + * default?: string | string[], + * default?: string | string[], + * worker?: string | string[], + * browser?: string | string[] + * }} PackageExports + + /** + * The default package index file such as 'index.js' + * @type {string} + */ + export const DEFAULT_PACKAGE_INDEX: string; + /** + * The default package manifest file name such as 'package.json' + * @type {string} + */ + export const DEFAULT_PACKAGE_MANIFEST_FILE_NAME: string; + /** + * The default package path prefix such as 'node_modules/' + * @type {string} + */ + export const DEFAULT_PACKAGE_PREFIX: string; + /** + * The default package version, when one is not provided + * @type {string} + */ + export const DEFAULT_PACKAGE_VERSION: string; + /** + * The default license for a package' + * @type {string} + */ + export const DEFAULT_LICENSE: string; + /** + * A container for a package name that includes a package organization identifier, + * its fully qualified name, or for relative package names, its pathname + */ + export class Name { + /** + * Parses a package name input resolving the actual module name, including an + * organization name given. If a path includes a manifest file + * ('package.json'), then the directory containing that file is considered a + * valid package and it will be included in the returned value. If a relative + * path is given, then the path is returned if it is a valid pathname. This + * function returns `null` for bad input. + * @param {string|URL} input + * @param {{ origin?: string | URL, manifest?: string }=} [options] + * @return {ParsedPackageName?} + */ + static parse(input: string | URL, options?: { + origin?: string | URL; + manifest?: string; + } | undefined): ParsedPackageName | null; + /** + * Returns `true` if the given `input` can be parsed by `Name.parse` or given + * as input to the `Name` class constructor. + * @param {string|URL} input + * @param {{ origin?: string | URL, manifest?: string }=} [options] + * @return {boolean} + */ + static canParse(input: string | URL, options?: { + origin?: string | URL; + manifest?: string; + } | undefined): boolean; + /** + * Creates a new `Name` from input. + * @param {string|URL} input + * @param {{ origin?: string | URL, manifest?: string }=} [options] + * @return {Name} + */ + static from(input: string | URL, options?: { + origin?: string | URL; + manifest?: string; + } | undefined): Name; + /** + * `Name` class constructor. + * @param {string|URL|NameOptions|Name} name + * @param {{ origin?: string | URL, manifest?: string }=} [options] + * @throws TypeError + */ + constructor(name: string | URL | NameOptions | Name, options?: { + origin?: string | URL; + manifest?: string; + } | undefined); + /** + * The id of this package name. + * @type {string} + */ + get id(): string; + /** + * The actual package name. + * @type {string} + */ + get name(): string; + /** + * An alias for 'name'. + * @type {string} + */ + get value(): string; + /** + * The origin of the package, if available. + * This value may be `null`. + * @type {string?} + */ + get origin(): string; + /** + * The package version if available. + * This value may be `null`. + * @type {string?} + */ + get version(): string; + /** + * The actual package pathname, if given in name string. + * This value is always a string defaulting to '.' if no path + * was given in name string. + * @type {string} + */ + get pathname(): string; + /** + * The organization name. + * This value may be `null`. + * @type {string?} + */ + get organization(): string; + /** + * `true` if the package name was relative, otherwise `false`. + * @type {boolean} + */ + get isRelative(): boolean; + /** + * Converts this package name to a string. + * @ignore + * @return {string} + */ + toString(): string; + /** + * Converts this `Name` instance to JSON. + * @ignore + * @return {object} + */ + toJSON(): object; + #private; + } + /** + * A container for package dependencies that map a package name to a `Package` instance. + */ + export class Dependencies { + constructor(parent: any, options?: any); + get map(): Map; + get origin(): any; + add(name: any, info?: any): void; + get(name: any, options?: any): any; + entries(): IterableIterator<[any, any]>; + keys(): IterableIterator; + values(): IterableIterator; + load(options?: any): void; + [Symbol.iterator](): IterableIterator<[any, any]>; + #private; + } + /** + * A container for CommonJS module metadata, often in a `package.json` file. + */ + export class Package { + /** + * A high level class for a package name. + * @type {typeof Name} + */ + static Name: typeof Name; + /** + * A high level container for package dependencies. + * @type {typeof Dependencies} + */ + static Dependencies: typeof Dependencies; + /** + * Creates and loads a package + * @param {string|URL|NameOptions|Name} name + * @param {PackageOptions & PackageLoadOptions=} [options] + * @return {Package} + */ + static load(name: string | URL | NameOptions | Name, options?: (PackageOptions & PackageLoadOptions) | undefined): Package; + /** + * `Package` class constructor. + * @param {string|URL|NameOptions|Name} name + * @param {PackageOptions=} [options] + */ + constructor(name: string | URL | NameOptions | Name, options?: PackageOptions | undefined); + /** + * The unique ID of this `Package`, which is the absolute + * URL of the directory that contains its manifest file. + * @type {string} + */ + get id(): string; + /** + * The absolute URL to the package manifest file + * @type {string} + */ + get url(): string; + /** + * A reference to the package subpath imports and browser mappings. + * These values are typically used with its corresponding `Module` + * instance require resolvers. + * @type {object} + */ + get imports(): any; + /** + * A loader for this package, if available. This value may be `null`. + * @type {Loader} + */ + get loader(): Loader; + /** + * `true` if the package was actually "loaded", otherwise `false`. + * @type {boolean} + */ + get loaded(): boolean; + /** + * The name of the package. + * @type {string} + */ + get name(): string; + /** + * The description of the package. + * @type {string} + */ + get description(): string; + /** + * The organization of the package. This value may be `null`. + * @type {string?} + */ + get organization(): string; + /** + * The license of the package. + * @type {string} + */ + get license(): string; + /** + * The version of the package. + * @type {string} + */ + get version(): string; + /** + * The origin for this package. + * @type {string} + */ + get origin(): string; + /** + * The exports mappings for the package + * @type {object} + */ + get exports(): any; + /** + * The package type. + * @type {'commonjs'|'module'} + */ + get type(): "module" | "commonjs"; + /** + * The raw package metadata object. + * @type {object?} + */ + get info(): any; + /** + * @type {Dependencies} + */ + get dependencies(): Dependencies; + /** + * An alias for `entry` + * @type {string?} + */ + get main(): string; + /** + * The entry to the package + * @type {string?} + */ + get entry(): string; + /** + * Load the package information at an optional `origin` with + * optional request `options`. + * @param {PackageLoadOptions=} [options] + * @throws SyntaxError + * @return {boolean} + */ + load(origin?: any, options?: PackageLoadOptions | undefined): boolean; + /** + * Resolve a file's `pathname` within the package. + * @param {string|URL} pathname + * @param {PackageResolveOptions=} [options] + * @return {string} + */ + resolve(pathname: string | URL, options?: PackageResolveOptions | undefined): string; + #private; + } + export default Package; + export type PackageOptions = { + manifest?: string; + index?: string; + description?: string; + version?: string; + license?: string; + exports?: object; + type?: "commonjs" | "module"; + info?: object; + origin?: string; + dependencies?: Dependencies | object | Map; + }; + export type PackageLoadOptions = import("commonjs/loader").RequestOptions & { + type?: "commonjs" | "module"; + prefix?: string; + }; + export type ParsedPackageName = { + organization: string | null; + name: string; + version: string | null; + pathname: string; + url: URL; + isRelative: boolean; + hasManifest: boolean; + }; + /** + * /** + * The default package index file such as 'index.js' + */ + export type PackageExports = { + require?: string | string[]; + import?: string | string[]; + default?: string | string[]; + default?: string | string[]; + worker?: string | string[]; + browser?: string | string[]; + }; + import URL from "url"; + import { Loader } from "commonjs/loader"; +} +declare module "commonjs/module" { + /** + * CommonJS module scope with module scoped globals. + * @ignore + * @param {object} exports + * @param {function(string): any} require + * @param {Module} module + * @param {string} __filename + * @param {string} __dirname + * @param {typeof process} process + * @param {object} global + */ + export function CommonJSModuleScope(exports: object, require: (arg0: string) => any, module: Module, __filename: string, __dirname: string, process: any, global: object): void; + /** + * Creates a `require` function from a given module URL. + * @param {string|URL} url + * @param {ModuleOptions=} [options] + * @return {RequireFunction} + */ + export function createRequire(url: string | URL, options?: ModuleOptions | undefined): RequireFunction; + /** + * @typedef {function(string, Module, function(string): any): any} ModuleResolver + */ + /** + * @typedef {import('./require.js').RequireFunction} RequireFunction + */ + /** + * @typedef {import('./package.js').PackageOptions} PackageOptions + */ + /** + * @typedef {{ + * prefix?: string, + * request?: import('./loader.js').RequestOptions, + * builtins?: object + * } CreateRequireOptions + */ + /** + * @typedef {{ + * resolvers?: ModuleResolver[], + * importmap?: ImportMap, + * loader?: Loader | object, + * loaders?: object, + * package?: Package | PackageOptions + * parent?: Module, + * state?: State + * }} ModuleOptions + */ + /** + * @typedef {{ + * extensions?: object + * }} ModuleLoadOptions + */ + export const builtinModules: any; + /** + * CommonJS module scope source wrapper. + * @type {string} + */ + export const COMMONJS_WRAPPER: string; + /** + * A container for imports. + * @see {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap} + */ + export class ImportMap { + set imports(imports: any); + /** + * The imports object for the importmap. + * @type {object} + */ + get imports(): any; + /** + * Extends the current imports object. + * @param {object} imports + * @return {ImportMap} + */ + extend(importmap: any): ImportMap; + #private; + } + /** + * A container for `Module` instance state. + */ + export class State { + /** + * `State` class constructor. + * @ignore + * @param {object|State=} [state] + */ + constructor(state?: (object | State) | undefined); + loading: boolean; + loaded: boolean; + error: any; + } + /** + * The module scope for a loaded module. + * This is a special object that is seal, frozen, and only exposes an + * accessor the 'exports' field. + * @ignore + */ + export class ModuleScope { + /** + * `ModuleScope` class constructor. + * @param {Module} module + */ + constructor(module: Module); + get id(): any; + get filename(): any; + get loaded(): any; + get children(): any; + set exports(exports: any); + get exports(): any; + toJSON(): { + id: any; + filename: any; + children: any; + exports: any; + }; + #private; + } + /** + * An abstract base class for loading a module. + */ + export class ModuleLoader { + /** + * Creates a `ModuleLoader` instance from the `module` currently being loaded. + * @param {Module} module + * @param {ModuleLoadOptions=} [options] + * @return {ModuleLoader} + */ + static from(module: Module, options?: ModuleLoadOptions | undefined): ModuleLoader; + /** + * Creates a new `ModuleLoader` instance from the `module` currently + * being loaded with the `source` string to parse and load with optional + * `ModuleLoadOptions` options. + * @param {Module} module + * @param {ModuleLoadOptions=} [options] + * @return {boolean} + */ + static load(module: Module, options?: ModuleLoadOptions | undefined): boolean; + /** + * @param {Module} module + * @param {ModuleLoadOptions=} [options] + * @return {boolean} + */ + load(module: Module, options?: ModuleLoadOptions | undefined): boolean; + } + /** + * A JavaScript module loader + */ + export class JavaScriptModuleLoader extends ModuleLoader { + } + /** + * A JSON module loader. + */ + export class JSONModuleLoader extends ModuleLoader { + } + /** + * A WASM module loader + + */ + export class WASMModuleLoader extends ModuleLoader { + } + /** + * A container for a loaded CommonJS module. All errors bubble + * to the "main" module and global object (if possible). + */ + export class Module extends EventTarget { + /** + * A reference to the currently scoped module. + * @type {Module?} + */ + static current: Module | null; + /** + * A reference to the previously scoped module. + * @type {Module?} + */ + static previous: Module | null; + /** + * A cache of loaded modules + * @type {Map} + */ + static cache: Map; + /** + * An array of globally available module loader resolvers. + * @type {ModuleResolver[]} + */ + static resolvers: ModuleResolver[]; + /** + * Globally available 'importmap' for all loaded modules. + * @type {ImportMap} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap} + */ + static importmap: ImportMap; + /** + * A limited set of builtins exposed to CommonJS modules. + * @type {object} + */ + static builtins: object; + /** + * A limited set of builtins exposed to CommonJS modules. + * @type {object} + */ + static builtinModules: object; + /** + * CommonJS module scope source wrapper components. + * @type {string[]} + */ + static wrapper: string[]; + /** + * An array of global require paths, relative to the origin. + * @type {string[]} + */ + static globalPaths: string[]; + /** + * Globabl module loaders + * @type {object} + */ + static loaders: object; + /** + * The main entry module, lazily created. + * @type {Module} + */ + static get main(): Module; + /** + * Wraps source in a CommonJS module scope. + * @param {string} source + */ + static wrap(source: string): string; + /** + * Compiles given JavaScript module source. + * @param {string} source + * @param {{ url?: URL | string }=} [options] + * @return {function( + * object, + * function(string): any, + * Module, + * string, + * string, + * typeof process, + * object + * ): any} + */ + static compile(source: string, options?: { + url?: URL | string; + } | undefined): (arg0: object, arg1: (arg0: string) => any, arg2: Module, arg3: string, arg4: string, arg5: typeof process, arg6: object) => any; + /** + * Creates a `Module` from source URL and optionally a parent module. + * @param {string|URL|Module} url + * @param {ModuleOptions=} [options] + */ + static from(url: string | URL | Module, options?: ModuleOptions | undefined): any; + /** + * Creates a `require` function from a given module URL. + * @param {string|URL} url + * @param {ModuleOptions=} [options] + */ + static createRequire(url: string | URL, options?: ModuleOptions | undefined): any; + /** + * `Module` class constructor. + * @param {string|URL} url + * @param {ModuleOptions=} [options] + */ + constructor(url: string | URL, options?: ModuleOptions | undefined); + /** + * A unique ID for this module. + * @type {string} + */ + get id(): string; + /** + * A reference to the "main" module. + * @type {Module} + */ + get main(): Module; + /** + * Child modules of this module. + * @type {Module[]} + */ + get children(): Module[]; + /** + * A reference to the module cache. Possibly shared with all + * children modules. + * @type {object} + */ + get cache(): any; + /** + * A reference to the module package. + * @type {Package} + */ + get package(): Package; + /** + * The `ImportMap` for this module. + * @type {ImportMap} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap} + */ + get importmap(): ImportMap; + /** + * The module level resolvers. + * @type {ModuleResolver[]} + */ + get resolvers(): ModuleResolver[]; + /** + * `true` if the module is currently loading, otherwise `false`. + * @type {boolean} + */ + get loading(): boolean; + /** + * `true` if the module is currently loaded, otherwise `false`. + * @type {boolean} + */ + get loaded(): boolean; + /** + * An error associated with the module if it failed to load. + * @type {Error?} + */ + get error(): Error; + /** + * The exports of the module + * @type {object} + */ + get exports(): any; + /** + * The scope of the module given to parsed modules. + * @type {ModuleScope} + */ + get scope(): ModuleScope; + /** + * The origin of the loaded module. + * @type {string} + */ + get origin(): string; + /** + * The parent module for this module. + * @type {Module?} + */ + get parent(): Module; + /** + * The `Loader` for this module. + * @type {Loader} + */ + get loader(): Loader; + /** + * The filename of the module. + * @type {string} + */ + get filename(): string; + /** + * Known source loaders for this module keyed by file extension. + * @type {object} + */ + get loaders(): any; + /** + * Factory for creating a `require()` function based on a module context. + * @param {CreateRequireOptions=} [options] + * @return {RequireFunction} + */ + createRequire(options?: CreateRequireOptions | undefined): RequireFunction; + /** + * Creates a `Module` from source the URL with this module as + * the parent. + * @param {string|URL|Module} url + * @param {ModuleOptions=} [options] + */ + createModule(url: string | URL | Module, options?: ModuleOptions | undefined): any; + /** + * Requires a module at for a given `input` which can be a relative file, + * named module, or an absolute URL within the context of this odule. + * @param {string|URL} input + * @param {RequireOptions=} [options] + * @throws ModuleNotFoundError + * @throws ReferenceError + * @throws SyntaxError + * @throws TypeError + * @return {any} + */ + require(url: any, options?: RequireOptions | undefined): any; + /** + * Loads the module + * @param {ModuleLoadOptions=} [options] + * @return {boolean} + */ + load(options?: ModuleLoadOptions | undefined): boolean; + resolve(input: any): string; + /** + * @ignore + */ + [Symbol.toStringTag](): string; + #private; + } + export namespace Module { + export { Module }; + } + export default Module; + export type ModuleResolver = (arg0: string, arg1: Module, arg2: (arg0: string) => any) => any; + export type RequireFunction = import("commonjs/require").RequireFunction; + export type PackageOptions = import("commonjs/package").PackageOptions; + export type CreateRequireOptions = { + prefix?: string; + request?: import("commonjs/loader").RequestOptions; + builtins?: object; + }; + export type ModuleOptions = { + resolvers?: ModuleResolver[]; + importmap?: ImportMap; + loader?: Loader | object; + loaders?: object; + package?: Package | PackageOptions; + parent?: Module; + state?: State; + }; + export type ModuleLoadOptions = { + extensions?: object; + }; + import { Package } from "commonjs/package"; + import { Loader } from "commonjs/loader"; + import process from "process"; +} +declare module "commonjs/require" { + /** + * Factory for creating a `require()` function based on a module context. + * @param {CreateRequireOptions} options + * @return {RequireFunction} + */ + export function createRequire(options: CreateRequireOptions): RequireFunction; + /** + * @typedef {function(string, import('./module.js').Module, function(string): any): any} RequireResolver + */ + /** + * @typedef {{ + * module: import('./module.js').Module, + * prefix?: string, + * request?: import('./loader.js').RequestOptions, + * builtins?: object, + * resolvers?: RequireFunction[] + * }} CreateRequireOptions + */ + /** + * @typedef {function(string): any} RequireFunction + */ + /** + * @typedef {import('./package.js').PackageOptions} PackageOptions + */ + /** + * @typedef {import('./package.js').PackageResolveOptions} PackageResolveOptions + */ + /** + * @typedef { + * PackageResolveOptions & + * PackageOptions & + * { origins?: string[] | URL[] } + * } ResolveOptions + */ + /** + * @typedef {ResolveOptions & { + * resolvers?: RequireResolver[], + * importmap?: import('./module.js').ImportMap, + * cache?: boolean + * }} RequireOptions + */ + /** + * An array of global require paths, relative to the origin. + * @type {string[]} + */ + export const globalPaths: string[]; + /** + * An object attached to a `require()` function that contains metadata + * about the current module context. + */ + export class Meta { + /** + * `Meta` class constructor. + * @param {import('./module.js').Module} module + */ + constructor(module: import("commonjs/module").Module); + /** + * The referrer (parent) of this module. + * @type {string} + */ + get referrer(): string; + /** + * The referrer (parent) of this module. + * @type {string} + */ + get url(): string; + #private; + } + export default createRequire; + export type RequireResolver = (arg0: string, arg1: import("commonjs/module").Module, arg2: (arg0: string) => any) => any; + export type CreateRequireOptions = { + module: import("commonjs/module").Module; + prefix?: string; + request?: import("commonjs/loader").RequestOptions; + builtins?: object; + resolvers?: RequireFunction[]; + }; + export type RequireFunction = (arg0: string) => any; + export type PackageOptions = import("commonjs/package").PackageOptions; + export type PackageResolveOptions = import("commonjs/package").PackageResolveOptions; + export type RequireOptions = ResolveOptions & { + resolvers?: RequireResolver[]; + importmap?: import("commonjs/module").ImportMap; + cache?: boolean; + }; +} +declare module "commonjs" { + export default exports; + import * as exports from "commonjs"; + import builtins from "commonjs/builtins"; + import Cache from "commonjs/cache"; + import createRequire from "commonjs/require"; + import Loader from "commonjs/loader"; + import Module from "commonjs/module"; + import Package from "commonjs/package"; + namespace ___home_werle_repos_socketsupply_socket_api_commonjs_ { } + export { builtins, Cache, createRequire, Loader, Module, Package }; +} +declare module "fetch/fetch" { + export function Headers(headers: any): void; + export class Headers { + constructor(headers: any); + map: {}; + append(name: any, value: any): void; + delete(name: any): void; + get(name: any): any; + has(name: any): boolean; + set(name: any, value: any): void; + forEach(callback: any, thisArg: any): void; + keys(): { + next: () => { + done: boolean; + value: any; + }; + }; + values(): { + next: () => { + done: boolean; + value: any; + }; + }; + entries(): { + next: () => { + done: boolean; + value: any; + }; + }; + } + export function Request(input: any, options: any): void; + export class Request { + constructor(input: any, options: any); + url: string; + credentials: any; + headers: Headers; + method: any; + mode: any; + signal: any; + referrer: any; + clone(): Request; + } + export function Response(bodyInit: any, options: any): void; + export class Response { + constructor(bodyInit: any, options: any); + type: string; + status: any; + ok: boolean; + statusText: string; + headers: Headers; + url: any; + clone(): Response; + } + export namespace Response { + function error(): Response; + function redirect(url: any, status: any): Response; + } + export function fetch(input: any, init: any): Promise; + export class DOMException { + private constructor(); + } + namespace _default { + export { fetch }; + export { Headers }; + export { Request }; + export { Response }; + } + export default _default; +} +declare module "fetch/index" { + export default fetch; + import { fetch } from "fetch/fetch"; + import { Headers } from "fetch/fetch"; + import { Request } from "fetch/fetch"; + import { Response } from "fetch/fetch"; + export { fetch, Headers, Request, Response }; +} +declare module "fetch" { + export * from "fetch/index"; + export default fetch; + import fetch from "fetch/index"; +} +declare module "i18n" { + /** + * Get messages for `locale` pattern. This function could return many results + * for various locales given a `locale` pattern. such as `fr`, which could + * return results for `fr`, `fr-FR`, `fr-BE`, etc. + * @ignore + * @param {string} locale + * @return {object[]} + */ + export function getMessagesForLocale(locale: string): object[]; + /** + * Returns user preferred ISO 639 language codes or RFC 5646 language tags. + * @return {string[]} + */ + export function getAcceptLanguages(): string[]; + /** + * Returns the current user ISO 639 language code or RFC 5646 language tag. + * @return {?string} + */ + export function getUILanguage(): string | null; + /** + * Gets a localized message string for the specified message name. + * @param {string} messageName + * @param {object|string[]=} [substitutions = []] + * @param {object=} [options] + * @param {string=} [options.locale = null] + * @see {@link https://developer.chrome.com/docs/extensions/reference/i18n/#type-LanguageCode} + * @see {@link https://www.ibm.com/docs/en/rbd/9.5.1?topic=syslib-getmessage} + * @return {?string} + */ + export function getMessage(messageName: string, substitutions?: (object | string[]) | undefined, options?: object | undefined): string | null; + /** + * Gets a localized message description string for the specified message name. + * @param {string} messageName + * @param {object=} [options] + * @param {string=} [options.locale = null] + * @return {?string} + */ + export function getMessageDescription(messageName: string, options?: object | undefined): string | null; + /** + * A cache of loaded locale messages. + * @type {Map} + */ + export const cache: Map; + /** + * Default location of i18n locale messages + * @type {string} + */ + export const DEFAULT_LOCALES_LOCATION: string; + /** + * An enumeration of supported ISO 639 language codes or RFC 5646 language tags. + * @type {Enumeration} + * @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n/LanguageCode} + * @see {@link https://developer.chrome.com/docs/extensions/reference/i18n/#type-LanguageCode} + */ + export const LanguageCode: Enumeration; + namespace _default { + export { LanguageCode }; + export { getAcceptLanguages }; + export { getMessage }; + export { getUILanguage }; + } + export default _default; + import Enumeration from "enumeration"; +} +declare module "node/index" { + export default network; + export function network(options: any): Promise; + import { Cache } from "latica/index"; + import { sha256 } from "latica/index"; + import { Encryption } from "latica/index"; + import { Packet } from "latica/index"; + import { NAT } from "latica/index"; + export { Cache, sha256, Encryption, Packet, NAT }; +} +declare module "index" { + import { network } from "node/index"; + import { Cache } from "node/index"; + import { sha256 } from "node/index"; + import { Encryption } from "node/index"; + import { Packet } from "node/index"; + import { NAT } from "node/index"; + export { network, Cache, sha256, Encryption, Packet, NAT }; +} +declare module "latica" { + export * from "latica/index"; + export default def; + import def from "latica/index"; +} +declare module "module" { + export const builtinModules: any; + export default Module; + export type ModuleOptions = import("commonjs/module").ModuleOptions; + export type ModuleResolver = import("commonjs/module").ModuleResolver; + export type ModuleLoadOptions = import("commonjs/module").ModuleLoadOptions; + export type RequireFunction = import("commonjs/module").RequireFunction; + export type CreateRequireOptions = import("commonjs/module").CreateRequireOptions; + import { createRequire } from "commonjs/module"; + import { Module } from "commonjs/module"; + import builtins from "commonjs/builtins"; + import { isBuiltin } from "commonjs/builtins"; + export { createRequire, Module, builtins, isBuiltin }; +} +declare module "node-esm-loader" { + export function resolve(specifier: any, ctx: any, next: any): Promise; + export default resolve; +} +declare module "internal/permissions" { + /** + * Query for a permission status. + * @param {PermissionDescriptor} descriptor + * @param {object=} [options] + * @param {?AbortSignal} [options.signal = null] + * @return {Promise} + */ + export function query(descriptor: PermissionDescriptor, options?: object | undefined, ...args: any[]): Promise; + /** + * Request a permission to be granted. + * @param {PermissionDescriptor} descriptor + * @param {object=} [options] + * @param {?AbortSignal} [options.signal = null] + * @return {Promise} + */ + export function request(descriptor: PermissionDescriptor, options?: object | undefined, ...args: any[]): Promise; + /** + * An enumeration of the permission types. + * - 'geolocation' + * - 'notifications' + * - 'push' + * - 'persistent-storage' + * - 'midi' + * - 'storage-access' + * @type {Enumeration} + * @ignore + */ + export const types: Enumeration; + const _default: any; + export default _default; + export type PermissionDescriptor = { + name: string; + }; + /** + * A container that provides the state of an object and an event handler + * for monitoring changes permission changes. + * @ignore + */ + class PermissionStatus extends EventTarget { + /** + * `PermissionStatus` class constructor. + * @param {string} name + * @param {string} initialState + * @param {object=} [options] + * @param {?AbortSignal} [options.signal = null] + */ + constructor(name: string, initialState: string, options?: object | undefined); + /** + * The name of this permission this status is for. + * @type {string} + */ + get name(): string; + /** + * The current state of the permission status. + * @type {string} + */ + get state(): string; + set onchange(onchange: (arg0: Event) => any); + /** + * Level 0 event target 'change' event listener accessor + * @type {function(Event)} + */ + get onchange(): (arg0: Event) => any; + /** + * Non-standard method for unsubscribing to status state updates. + * @ignore + */ + unsubscribe(): void; + /** + * String tag for `PermissionStatus`. + * @ignore + */ + get [Symbol.toStringTag](): string; + #private; + } + import Enumeration from "enumeration"; +} +declare module "notification" { + /** + * Show a notification. Creates a `Notification` instance and displays + * it to the user. + * @param {string} title + * @param {NotificationOptions=} [options] + * @param {function(Event)=} [onclick] + * @param {function(Event)=} [onclose] + * @return {Promise} + */ + export function showNotification(title: string, options?: NotificationOptions | undefined, onclick?: ((arg0: Event) => any) | undefined, onshow?: any): Promise; + /** + * The global event dispatched when a `Notification` is presented to + * the user. + * @ignore + * @type {string} + */ + export const NOTIFICATION_PRESENTED_EVENT: string; + /** + * The global event dispatched when a `Notification` has a response + * from the user. + * @ignore + * @type {string} + */ + export const NOTIFICATION_RESPONSE_EVENT: string; + /** + * An enumeratino of notification test directions: + * - 'auto' Automatically determined by the operating system + * - 'ltr' Left-to-right text direction + * - 'rtl' Right-to-left text direction + * @type {Enumeration} + * @ignore + */ + export const NotificationDirection: Enumeration; + /** + * An enumeration of permission types granted by the user for the current + * origin to display notifications to the end user. + * - 'granted' The user has explicitly granted permission for the current + * origin to display system notifications. + * - 'denied' The user has explicitly denied permission for the current + * origin to display system notifications. + * - 'default' The user decision is unknown; in this case the application + * will act as if permission was denied. + * @type {Enumeration} + * @ignore + */ + export const NotificationPermission: Enumeration; + /** + * A validated notification action object container. + * You should never need to construct this. + * @ignore + */ + export class NotificationAction { + /** + * `NotificationAction` class constructor. + * @ignore + * @param {object} options + * @param {string} options.action + * @param {string} options.title + * @param {string|URL=} [options.icon = ''] + */ + constructor(options: { + action: string; + title: string; + icon?: (string | URL) | undefined; + }); + /** + * A string identifying a user action to be displayed on the notification. + * @type {string} + */ + get action(): string; + /** + * A string containing action text to be shown to the user. + * @type {string} + */ + get title(): string; + /** + * A string containing the URL of an icon to display with the action. + * @type {string} + */ + get icon(): string; + #private; + } + /** + * A validated notification options object container. + * You should never need to construct this. + * @ignore + */ + export class NotificationOptions { + /** + * `NotificationOptions` class constructor. + * @ignore + * @param {object} [options = {}] + * @param {string=} [options.dir = 'auto'] + * @param {NotificationAction[]=} [options.actions = []] + * @param {string|URL=} [options.badge = ''] + * @param {string=} [options.body = ''] + * @param {?any=} [options.data = null] + * @param {string|URL=} [options.icon = ''] + * @param {string|URL=} [options.image = ''] + * @param {string=} [options.lang = ''] + * @param {string=} [options.tag = ''] + * @param {boolean=} [options.boolean = ''] + * @param {boolean=} [options.requireInteraction = false] + * @param {boolean=} [options.silent = false] + * @param {number[]=} [options.vibrate = []] + */ + constructor(options?: { + dir?: string | undefined; + actions?: NotificationAction[] | undefined; + badge?: (string | URL) | undefined; + body?: string | undefined; + data?: (any | null) | undefined; + icon?: (string | URL) | undefined; + image?: (string | URL) | undefined; + lang?: string | undefined; + tag?: string | undefined; + boolean?: boolean | undefined; + requireInteraction?: boolean | undefined; + silent?: boolean | undefined; + vibrate?: number[] | undefined; + }, allowServiceWorkerGlobalScope?: boolean); + /** + * An array of actions to display in the notification. + * @type {NotificationAction[]} + */ + get actions(): NotificationAction[]; + /** + * A string containing the URL of the image used to represent + * the notification when there isn't enough space to display the + * notification itself. + * @type {string} + */ + get badge(): string; + /** + * A string representing the body text of the notification, + * which is displayed below the title. + * @type {string} + */ + get body(): string; + /** + * Arbitrary data that you want associated with the notification. + * This can be of any data type. + * @type {?any} + */ + get data(): any; + /** + * The direction in which to display the notification. + * It defaults to 'auto', which just adopts the environments + * language setting behavior, but you can override that behavior + * by setting values of 'ltr' and 'rtl'. + * @type {'auto'|'ltr'|'rtl'} + */ + get dir(): "auto" | "ltr" | "rtl"; + /** + A string containing the URL of an icon to be displayed in the notification. + * @type {string} + */ + get icon(): string; + /** + * The URL of an image to be displayed as part of the notification, as + * specified in the constructor's options parameter. + * @type {string} + */ + get image(): string; + /** + * The notification's language, as specified using a string representing a + * language tag according to RFC 5646. + * @type {string} + */ + get lang(): string; + /** + * A boolean value specifying whether the user should be notified after a + * new notification replaces an old one. The default is `false`, which means + * they won't be notified. If `true`, then tag also must be set. + * @type {boolean} + */ + get renotify(): boolean; + /** + * Indicates that a notification should remain active until the user clicks + * or dismisses it, rather than closing automatically. + * The default value is `false`. + * @type {boolean} + */ + get requireInteraction(): boolean; + /** + * A boolean value specifying whether the notification is silent (no sounds + * or vibrations issued), regardless of the device settings. + * The default is `false`, which means it won't be silent. If `true`, then + * vibrate must not be present. + * @type {boolean} + */ + get silent(): boolean; + /** + * A string representing an identifying tag for the notification. + * The default is the empty string. + * @type {string} + */ + get tag(): string; + /** + * A vibration pattern for the device's vibration hardware to emit with + * the notification. If specified, silent must not be `true`. + * @type {number[]} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Vibration_API#vibration_patterns} + */ + get vibrate(): number[]; + /** + * @ignore + * @return {object} + */ + toJSON(): object; + #private; + } + /** + * The Notification interface is used to configure and display + * desktop and mobile notifications to the user. + */ + export class Notification extends EventTarget { + /** + * A read-only property that indicates the current permission granted + * by the user to display notifications. + * @type {'prompt'|'granted'|'denied'} + */ + static get permission(): "denied" | "granted" | "prompt"; + /** + * The maximum number of actions supported by the device. + * @type {number} + */ + static get maxActions(): number; + /** + * Requests permission from the user to display notifications. + * @param {object=} [options] + * @param {boolean=} [options.alert = true] - (macOS/iOS only) + * @param {boolean=} [options.sound = false] - (macOS/iOS only) + * @param {boolean=} [options.badge = false] - (macOS/iOS only) + * @param {boolean=} [options.force = false] + * @return {Promise<'granted'|'default'|'denied'>} + */ + static requestPermission(options?: object | undefined): Promise<"granted" | "default" | "denied">; + /** + * `Notification` class constructor. + * @param {string} title + * @param {NotificationOptions=} [options] + */ + constructor(title: string, options?: NotificationOptions | undefined, existingState?: any, ...args: any[]); + /** + * @ignore + */ + get options(): any; + /** + * A unique identifier for this notification. + * @type {string} + */ + get id(): string; + /** + * `true` if the notification was closed, otherwise `false`. + * @type {boolea} + */ + get closed(): boolea; + set onclick(onclick: Function); + /** + * The click event is dispatched when the user clicks on + * displayed notification. + * @type {?function} + */ + get onclick(): Function; + set onclose(onclose: Function); + /** + * The close event is dispatched when the notification closes. + * @type {?function} + */ + get onclose(): Function; + set onerror(onerror: Function); + /** + * The eror event is dispatched when the notification fails to display + * or encounters an error. + * @type {?function} + */ + get onerror(): Function; + set onshow(onshow: Function); + /** + * The click event is dispatched when the notification is displayed. + * @type {?function} + */ + get onshow(): Function; + /** + * An array of actions to display in the notification. + * @type {NotificationAction[]} + */ + get actions(): NotificationAction[]; + /** + * A string containing the URL of the image used to represent + * the notification when there isn't enough space to display the + * notification itself. + * @type {string} + */ + get badge(): string; + /** + * A string representing the body text of the notification, + * which is displayed below the title. + * @type {string} + */ + get body(): string; + /** + * Arbitrary data that you want associated with the notification. + * This can be of any data type. + * @type {?any} + */ + get data(): any; + /** + * The direction in which to display the notification. + * It defaults to 'auto', which just adopts the environments + * language setting behavior, but you can override that behavior + * by setting values of 'ltr' and 'rtl'. + * @type {'auto'|'ltr'|'rtl'} + */ + get dir(): "auto" | "ltr" | "rtl"; + /** + * A string containing the URL of an icon to be displayed in the notification. + * @type {string} + */ + get icon(): string; + /** + * The URL of an image to be displayed as part of the notification, as + * specified in the constructor's options parameter. + * @type {string} + */ + get image(): string; + /** + * The notification's language, as specified using a string representing a + * language tag according to RFC 5646. + * @type {string} + */ + get lang(): string; + /** + * A boolean value specifying whether the user should be notified after a + * new notification replaces an old one. The default is `false`, which means + * they won't be notified. If `true`, then tag also must be set. + * @type {boolean} + */ + get renotify(): boolean; + /** + * Indicates that a notification should remain active until the user clicks + * or dismisses it, rather than closing automatically. + * The default value is `false`. + * @type {boolean} + */ + get requireInteraction(): boolean; + /** + * A boolean value specifying whether the notification is silent (no sounds + * or vibrations issued), regardless of the device settings. + * The default is `false`, which means it won't be silent. If `true`, then + * vibrate must not be present. + * @type {boolean} + */ + get silent(): boolean; + /** + * A string representing an identifying tag for the notification. + * The default is the empty string. + * @type {string} + */ + get tag(): string; + /** + * A vibration pattern for the device's vibration hardware to emit with + * the notification. If specified, silent must not be `true`. + * @type {number[]} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Vibration_API#vibration_patterns} + */ + get vibrate(): number[]; + /** + * The timestamp of the notification. + * @type {number} + */ + get timestamp(): number; + /** + * The title read-only property of the `Notification` instace indicates + * the title of the notification, as specified in the `title` parameter + * of the `Notification` constructor. + * @type {string} + */ + get title(): string; + /** + * Closes the notification programmatically. + */ + close(): Promise; + #private; + } + export default Notification; + import { Enumeration } from "enumeration"; + import URL from "url"; +} +declare module "protocol-handlers" { + /** + * @typedef {{ scheme: string }} GetServiceWorkerOptions + + /** + * @param {GetServiceWorkerOptions} options + * @return {Promise + */ + export function getServiceWorker(options: GetServiceWorkerOptions): Promise; + namespace _default { + export { getServiceWorker }; + } + export default _default; + /** + * /** + */ + export type GetServiceWorkerOptions = { + scheme: string; + }; +} +declare module "shared-worker" { + /** + * A reference to the opened environment. This value is an instance of an + * `Environment` if the scope is a ServiceWorker scope. + * @type {Environment|null} + */ + export const env: Environment | null; + export default SharedWorker; + import { SharedWorker } from "shared-worker/index"; + export { Environment, SharedWorker }; +} +declare module "signal" { + export * from "process/signal"; + export default signal; + import signal from "process/signal"; +} +declare module "service-worker/instance" { + export function createServiceWorker(currentState?: any, options?: any): any; + export const SHARED_WORKER_URL: string; + export const ServiceWorker: { + new (): ServiceWorker; + prototype: ServiceWorker; + } | { + new (): { + onmessage: any; + onerror: any; + onstatechange: any; + readonly state: any; + readonly scriptURL: any; + postMessage(): void; + addEventListener(type: string, callback: EventListenerOrEventListenerObject | null, options?: AddEventListenerOptions | boolean): void; + dispatchEvent(event: Event): boolean; + removeEventListener(type: string, callback: EventListenerOrEventListenerObject | null, options?: EventListenerOptions | boolean): void; + }; + }; + export default createServiceWorker; +} +declare module "worker" { + export default Worker; + import { SharedWorker } from "shared-worker/index"; + import { ServiceWorker } from "service-worker/instance"; + import { Worker } from "worker_threads"; + export { SharedWorker, ServiceWorker, Worker }; +} +declare module "child_process/worker" { + export {}; +} +declare module "internal/callsite" { + /** + * Creates an ordered and link array of `CallSite` instances from a + * given `Error`. + * @param {Error} error + * @param {string} source + * @return {CallSite[]} + */ + export function createCallSites(error: Error, source: string): CallSite[]; + /** + * @typedef {{ + * sourceURL: string | null, + * symbol: string, + * column: number | undefined, + * line: number | undefined, + * native: boolean + * }} ParsedStackFrame + */ + /** + * A container for location data related to a `StackFrame` + */ + export class StackFrameLocation { + /** + * Creates a `StackFrameLocation` from JSON input. + * @param {object=} json + * @return {StackFrameLocation} + */ + static from(json?: object | undefined): StackFrameLocation; + /** + * The line number of the location of the stack frame, if available. + * @type {number | undefined} + */ + lineNumber: number | undefined; + /** + * The column number of the location of the stack frame, if available. + * @type {number | undefined} + */ + columnNumber: number | undefined; + /** + * The source URL of the location of the stack frame, if available. This value + * may be `null`. + * @type {string?} + */ + sourceURL: string | null; + /** + * `true` if the stack frame location is in native location, otherwise + * this value `false` (default). + * @type + */ + isNative: any; + /** + * Converts this `StackFrameLocation` to a JSON object. + * @ignore + * @return {{ + * lineNumber: number | undefined, + * columnNumber: number | undefined, + * sourceURL: string | null, + * isNative: boolean + * }} + */ + toJSON(): { + lineNumber: number | undefined; + columnNumber: number | undefined; + sourceURL: string | null; + isNative: boolean; + }; + } + /** + * A stack frame container related to a `CallSite`. + */ + export class StackFrame { + /** + * Parses a raw stack frame string into structured data. + * @param {string} rawStackFrame + * @return {ParsedStackFrame} + */ + static parse(rawStackFrame: string): ParsedStackFrame; + /** + * Creates a new `StackFrame` from an `Error` and raw stack frame + * source `rawStackFrame`. + * @param {Error} error + * @param {string} rawStackFrame + * @return {StackFrame} + */ + static from(error: Error, rawStackFrame: string): StackFrame; + /** + * `StackFrame` class constructor. + * @param {Error} error + * @param {ParsedStackFrame=} [frame] + * @param {string=} [source] + */ + constructor(error: Error, frame?: ParsedStackFrame | undefined, source?: string | undefined); + /** + * The stack frame location data. + * @type {StackFrameLocation} + */ + location: StackFrameLocation; + /** + * The `Error` associated with this `StackFrame` instance. + * @type {Error?} + */ + error: Error | null; + /** + * The name of the function where the stack frame is located. + * @type {string?} + */ + symbol: string | null; + /** + * The raw stack frame source string. + * @type {string?} + */ + source: string | null; + /** + * Converts this `StackFrameLocation` to a JSON object. + * @ignore + * @return {{ + * location: { + * lineNumber: number | undefined, + * columnNumber: number | undefined, + * sourceURL: string | null, + * isNative: boolean + * }, + * isNative: boolean, + * symbol: string | null, + * source: string | null, + * error: { message: string, name: string, stack: string } | null + * }} + */ + toJSON(): { + location: { + lineNumber: number | undefined; + columnNumber: number | undefined; + sourceURL: string | null; + isNative: boolean; + }; + isNative: boolean; + symbol: string | null; + source: string | null; + error: { + message: string; + name: string; + stack: string; + } | null; + }; + } + /** + * A v8 compatible interface and container for call site information. + */ + export class CallSite { + /** + * An internal symbol used to refer to the index of a promise in + * `Promise.all` or `Promise.any` function call site. + * @ignore + * @type {symbol} + */ + static PromiseElementIndexSymbol: symbol; + /** + * An internal symbol used to indicate that a call site is in a `Promise.all` + * function call. + * @ignore + * @type {symbol} + */ + static PromiseAllSymbol: symbol; + /** + * An internal symbol used to indicate that a call site is in a `Promise.any` + * function call. + * @ignore + * @type {symbol} + */ + static PromiseAnySymbol: symbol; + /** + * An internal source symbol used to store the original `Error` stack source. + * @ignore + * @type {symbol} + */ + static StackSourceSymbol: symbol; + /** + * `CallSite` class constructor + * @param {Error} error + * @param {string} rawStackFrame + * @param {CallSite=} previous + */ + constructor(error: Error, rawStackFrame: string, previous?: CallSite | undefined); + /** + * The `Error` associated with the call site. + * @type {Error} + */ + get error(): Error; + /** + * The previous `CallSite` instance, if available. + * @type {CallSite?} + */ + get previous(): CallSite; + /** + * A reference to the `StackFrame` data. + * @type {StackFrame} + */ + get frame(): StackFrame; + /** + * This function _ALWAYS__ returns `globalThis` as `this` cannot be determined. + * @return {object} + */ + getThis(): object; + /** + * This function _ALWAYS__ returns `null` as the type name of `this` + * cannot be determined. + * @return {null} + */ + getTypeName(): null; + /** + * This function _ALWAYS__ returns `undefined` as the current function + * reference cannot be determined. + * @return {undefined} + */ + getFunction(): undefined; + /** + * Returns the name of the function in at the call site, if available. + * @return {string|undefined} + */ + getFunctionName(): string | undefined; + /** + * An alias to `getFunctionName() + * @return {string} + */ + getMethodName(): string; + /** + * Get the filename of the call site location, if available, otherwise this + * function returns 'unknown location'. + * @return {string} + */ + getFileName(): string; + /** + * Returns the location source URL defaulting to the global location. + * @return {string} + */ + getScriptNameOrSourceURL(): string; + /** + * Returns a hash value of the source URL return by `getScriptNameOrSourceURL()` + * @return {string} + */ + getScriptHash(): string; + /** + * Returns the line number of the call site location. + * This value may be `undefined`. + * @return {number|undefined} + */ + getLineNumber(): number | undefined; + /** + * @ignore + * @return {number} + */ + getPosition(): number; + /** + * Attempts to get an "enclosing" line number, potentially the previous + * line number of the call site + * @param {number|undefined} + */ + getEnclosingLineNumber(): any; + /** + * Returns the column number of the call site location. + * This value may be `undefined`. + * @return {number|undefined} + */ + getColumnNumber(): number | undefined; + /** + * Attempts to get an "enclosing" column number, potentially the previous + * line number of the call site + * @param {number|undefined} + */ + getEnclosingColumnNumber(): any; + /** + * Gets the origin of where `eval()` was called if this call site function + * originated from a call to `eval()`. This function may return `undefined`. + * @return {string|undefined} + */ + getEvalOrigin(): string | undefined; + /** + * This function _ALWAYS__ returns `false` as `this` cannot be determined so + * "top level" detection is not possible. + * @return {boolean} + */ + isTopLevel(): boolean; + /** + * Returns `true` if this call site originated from a call to `eval()`. + * @return {boolean} + */ + isEval(): boolean; + /** + * Returns `true` if the call site is in a native location, otherwise `false`. + * @return {boolean} + */ + isNative(): boolean; + /** + * This function _ALWAYS_ returns `false` as constructor detection + * is not possible. + * @return {boolean} + */ + isConstructor(): boolean; + /** + * Returns `true` if the call site is in async context, otherwise `false`. + * @return {boolean} + */ + isAsync(): boolean; + /** + * Returns `true` if the call site is in a `Promise.all()` function call, + * otherwise `false. + * @return {boolean} + */ + isPromiseAll(): boolean; + /** + * Gets the index of the promise element that was followed in a + * `Promise.all()` or `Promise.any()` function call. If not available, then + * this function returns `null`. + * @return {number|null} + */ + getPromiseIndex(): number | null; + /** + * Converts this call site to a string. + * @return {string} + */ + toString(): string; + /** + * Converts this `CallSite` to a JSON object. + * @ignore + * @return {{ + * frame: { + * location: { + * lineNumber: number | undefined, + * columnNumber: number | undefined, + * sourceURL: string | null, + * isNative: boolean + * }, + * isNative: boolean, + * symbol: string | null, + * source: string | null, + * error: { message: string, name: string, stack: string } | null + * } + * }} + */ + toJSON(): { + frame: { + location: { + lineNumber: number | undefined; + columnNumber: number | undefined; + sourceURL: string | null; + isNative: boolean; + }; + isNative: boolean; + symbol: string | null; + source: string | null; + error: { + message: string; + name: string; + stack: string; + } | null; + }; + }; + set [$previous](previous: any); + /** + * Private accessor to "friend class" `CallSiteList`. + * @ignore + */ + get [$previous](): any; + #private; + } + /** + * An array based list container for `CallSite` instances. + */ + export class CallSiteList extends Array { + /** + * Creates a `CallSiteList` instance from `Error` input. + * @param {Error} error + * @param {string} source + * @return {CallSiteList} + */ + static from(error: Error, source: string): CallSiteList; + /** + * `CallSiteList` class constructor. + * @param {Error} error + * @param {string[]=} [sources] + */ + constructor(error: Error, sources?: string[] | undefined); + /** + * A reference to the `Error` for this `CallSiteList` instance. + * @type {Error} + */ + get error(): Error; + /** + * An array of stack frame source strings. + * @type {string[]} + */ + get sources(): string[]; + /** + * The original stack string derived from the sources. + * @type {string} + */ + get stack(): string; + /** + * Adds `CallSite` instances to the top of the list, linking previous + * instances to the next one. + * @param {...CallSite} callsites + * @return {number} + */ + unshift(...callsites: CallSite[]): number; + /** + * A no-op function as `CallSite` instances cannot be added to the end + * of the list. + * @return {number} + */ + push(): number; + /** + * Pops a `CallSite` off the end of the list. + * @return {CallSite|undefined} + */ + pop(): CallSite | undefined; + /** + * Converts this `CallSiteList` to a JSON object. + * @return {{ + * frame: { + * location: { + * lineNumber: number | undefined, + * columnNumber: number | undefined, + * sourceURL: string | null, + * isNative: boolean + * }, + * isNative: boolean, + * symbol: string | null, + * source: string | null, + * error: { message: string, name: string, stack: string } | null + * } + * }[]} + */ + toJSON(): { + frame: { + location: { + lineNumber: number | undefined; + columnNumber: number | undefined; + sourceURL: string | null; + isNative: boolean; + }; + isNative: boolean; + symbol: string | null; + source: string | null; + error: { + message: string; + name: string; + stack: string; + } | null; + }; + }[]; + #private; + } + export default CallSite; + export type ParsedStackFrame = { + sourceURL: string | null; + symbol: string; + column: number | undefined; + line: number | undefined; + native: boolean; + }; + const $previous: unique symbol; +} +declare module "internal/error" { + /** + * The default `Error` class stack trace limit. + * @type {number} + */ + export const DEFAULT_ERROR_STACK_TRACE_LIMIT: number; + export const DefaultPlatformError: ErrorConstructor; + export const Error: ErrorConstructor; + export const URIError: ErrorConstructor; + export const EvalError: ErrorConstructor; + export const TypeError: ErrorConstructor; + export const RangeError: ErrorConstructor; + export const MediaError: ErrorConstructor; + export const SyntaxError: ErrorConstructor; + export const ReferenceError: ErrorConstructor; + export const AggregateError: ErrorConstructor; + export const RTCError: ErrorConstructor; + export const OverconstrainedError: ErrorConstructor; + export const GeolocationPositionError: ErrorConstructor; + export const ApplePayError: ErrorConstructor; + namespace _default { + export { Error }; + export { URIError }; + export { EvalError }; + export { TypeError }; + export { RangeError }; + export { MediaError }; + export { SyntaxError }; + export { ReferenceError }; + export { AggregateError }; + export { RTCError }; + export { OverconstrainedError }; + export { GeolocationPositionError }; + export { ApplePayError }; + } + export default _default; +} +declare module "internal/geolocation" { + /** + * Get the current position of the device. + * @param {function(GeolocationPosition)} onSuccess + * @param {onError(Error)} onError + * @param {object=} options + * @param {number=} options.timeout + * @return {Promise} + */ + export function getCurrentPosition(onSuccess: (arg0: GeolocationPosition) => any, onError: any, options?: object | undefined, ...args: any[]): Promise; + /** + * Register a handler function that will be called automatically each time the + * position of the device changes. You can also, optionally, specify an error + * handling callback function. + * @param {function(GeolocationPosition)} onSuccess + * @param {function(Error)} onError + * @param {object=} [options] + * @param {number=} [options.timeout = null] + * @return {number} + */ + export function watchPosition(onSuccess: (arg0: GeolocationPosition) => any, onError: (arg0: Error) => any, options?: object | undefined, ...args: any[]): number; + /** + * Unregister location and error monitoring handlers previously installed + * using `watchPosition`. + * @param {number} id + */ + export function clearWatch(id: number, ...args: any[]): any; + export namespace platform { + let getCurrentPosition: Function; + let watchPosition: Function; + let clearWatch: Function; + } + namespace _default { + export { getCurrentPosition }; + export { watchPosition }; + export { clearWatch }; + } + export default _default; +} +declare module "internal/post-message" { + const _default: any; + export default _default; +} +declare module "service-worker/notification" { + export function showNotification(registration: any, title: any, options: any): Promise; + export function getNotifications(registration: any, options?: any): Promise; + namespace _default { + export { showNotification }; + export { getNotifications }; + } + export default _default; +} +declare module "service-worker/registration" { + export class ServiceWorkerRegistration { + constructor(info: any, serviceWorker: any); + get scope(): any; + get updateViaCache(): string; + get installing(): any; + get waiting(): any; + get active(): any; + set onupdatefound(onupdatefound: any); + get onupdatefound(): any; + get navigationPreload(): any; + getNotifications(): Promise; + showNotification(title: any, options: any): Promise; + unregister(): Promise; + update(): Promise; + #private; + } + export default ServiceWorkerRegistration; +} +declare module "service-worker/container" { + /** + * Predicate to determine if service workers are allowed + * @return {boolean} + */ + export function isServiceWorkerAllowed(): boolean; + /** + * A `ServiceWorkerContainer` implementation that is attached to the global + * `globalThis.navigator.serviceWorker` object. + */ + export class ServiceWorkerContainer extends EventTarget { + get ready(): any; + get controller(): any; + /** + * A special initialization function for augmenting the global + * `globalThis.navigator.serviceWorker` platform `ServiceWorkerContainer` + * instance. + * + * All functions MUST be sure to what a lexically bound `this` becomes as the + * target could change with respect to the `internal` `Map` instance which + * contains private implementation properties relevant to the runtime + * `ServiceWorkerContainer` internal state implementations. + * @ignore + */ + init(): Promise; + register(scriptURL: any, options?: any): Promise; + getRegistration(clientURL: any): Promise; + getRegistrations(): Promise; + startMessages(): void; + } + export default ServiceWorkerContainer; + import { ServiceWorkerRegistration } from "service-worker/registration"; +} +declare module "internal/service-worker" { + export const serviceWorker: ServiceWorkerContainer; + export default serviceWorker; + import { ServiceWorkerContainer } from "service-worker/container"; +} +declare module "internal/webassembly" { + /** + * The `instantiateStreaming()` function compiles and instantiates a WebAssembly + * module directly from a streamed source. + * @ignore + * @param {Response} response + * @param {=object} [importObject] + * @return {Promise} + */ + export function instantiateStreaming(response: Response, importObject?: any): Promise; + /** + * The `compileStreaming()` function compiles and instantiates a WebAssembly + * module directly from a streamed source. + * @ignore + * @param {Response} response + * @return {Promise} + */ + export function compileStreaming(response: Response): Promise; + namespace _default { + export { instantiateStreaming }; + } + export default _default; +} +declare module "internal/scheduler" { + export * from "timers/scheduler"; + export default scheduler; + import scheduler from "timers/scheduler"; +} +declare module "internal/timers" { + export function setTimeout(callback: any, ...args: any[]): number; + export function clearTimeout(timeout: any): any; + export function setInterval(callback: any, ...args: any[]): number; + export function clearInterval(interval: any): any; + export function setImmediate(callback: any, ...args: any[]): number; + export function clearImmediate(immediate: any): any; + namespace _default { + export { setTimeout }; + export { setInterval }; + export { setImmediate }; + export { clearTimeout }; + export { clearInterval }; + export { clearImmediate }; + } + export default _default; +} +declare module "internal/pickers" { + /** + * @typedef {{ + * id?: string, + * mode?: 'read' | 'readwrite' + * startIn?: FileSystemHandle | 'desktop' | 'documents' | 'downloads' | 'music' | 'pictures' | 'videos', + * }} ShowDirectoryPickerOptions + * + */ + /** + * Shows a directory picker which allows the user to select a directory. + * @param {ShowDirectoryPickerOptions=} [options] + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/showDirectoryPicker} + * @return {Promise} + */ + export function showDirectoryPicker(options?: ShowDirectoryPickerOptions | undefined): Promise; + /** + * @typedef {{ + * id?: string, + * excludeAcceptAllOption?: boolean, + * startIn?: FileSystemHandle | 'desktop' | 'documents' | 'downloads' | 'music' | 'pictures' | 'videos', + * types?: Array<{ + * description?: string, + * [keyof object]?: string[] + * }> + * }} ShowOpenFilePickerOptions + */ + /** + * Shows a file picker that allows a user to select a file or multiple files + * and returns a handle for each selected file. + * @param {ShowOpenFilePickerOptions=} [options] + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/showOpenFilePicker} + * @return {Promise} + */ + export function showOpenFilePicker(options?: ShowOpenFilePickerOptions | undefined): Promise; + /** + * @typedef {{ + * id?: string, + * excludeAcceptAllOption?: boolean, + * suggestedName?: string, + * startIn?: FileSystemHandle | 'desktop' | 'documents' | 'downloads' | 'music' | 'pictures' | 'videos', + * types?: Array<{ + * description?: string, + * [keyof object]?: string[] + * }> + * }} ShowSaveFilePickerOptions + */ + /** + * Shows a file picker that allows a user to save a file by selecting an + * existing file, or entering a name for a new file. + * @param {ShowSaveFilePickerOptions=} [options] + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/showSaveFilePicker} + * @return {Promise} + */ + export function showSaveFilePicker(options?: ShowSaveFilePickerOptions | undefined): Promise; + /** + * Key-value store for general usage by the file pickers" + * @ignore + */ + export class Database { + get(key: any): any; + set(key: any, value: any): void; + } + /** + * Internal database for pickers, such as mapping IDs to directory/file paths. + * @ignore + */ + export const db: Database; + namespace _default { + export { showDirectoryPicker }; + export { showOpenFilePicker }; + export { showSaveFilePicker }; + } + export default _default; + export type ShowDirectoryPickerOptions = { + id?: string; + mode?: "read" | "readwrite"; + startIn?: FileSystemHandle | "desktop" | "documents" | "downloads" | "music" | "pictures" | "videos"; + }; + /** + * ]?: string[] + * }> + * }} ShowOpenFilePickerOptions + */ + export type object = { + id?: string; + excludeAcceptAllOption?: boolean; + startIn?: FileSystemHandle | "desktop" | "documents" | "downloads" | "music" | "pictures" | "videos"; + types?: Array<{ + description?: string; + [keyof]: any; + }>; + }; +} +declare module "internal/primitives" { + export function init(): { + natives: {}; + patches: {}; + }; + namespace _default { + export { natives }; + export { patches }; + } + export default _default; + const natives: {}; + const patches: {}; +} +declare module "internal/init" { + namespace _default { + export { location }; + } + export default _default; + import location from "location"; +} +declare module "internal/worker" { + export function onWorkerMessage(event: any): Promise; + export function addEventListener(eventName: any, callback: any, ...args: any[]): any; + export function removeEventListener(eventName: any, callback: any, ...args: any[]): any; + export function dispatchEvent(event: any): any; + export function postMessage(message: any, ...args: any[]): any; + export function close(): any; + export function importScripts(...scripts: any[]): void; + export const WorkerGlobalScopePrototype: any; + /** + * The absolute `URL` of the internal worker initialization entry. + * @ignore + * @type {URL} + */ + export const url: URL; + /** + * The worker entry source. + * @ignore + * @type {string} + */ + export const source: string; + /** + * A unique identifier for this worker made available on the global scope + * @ignore + * @type {string} + */ + export const RUNTIME_WORKER_ID: string; + /** + * Internally scoped event interface for a worker context. + * @ignore + * @type {object} + */ + export const worker: object; + /** + * A reference to the global worker scope. + * @type {WorkerGlobalScope} + */ + export const self: WorkerGlobalScope; + namespace _default { + export { RUNTIME_WORKER_ID }; + export { removeEventListener }; + export { addEventListener }; + export { importScripts }; + export { dispatchEvent }; + export { postMessage }; + export { source }; + export { close }; + export { url }; + } + export default _default; +} +declare module "latica/worker" { + export {}; +} +declare module "npm/module" { + /** + * @typedef {{ + * package: Package + * origin: string, + * type: 'commonjs' | 'module', + * url: string + * }} ModuleResolution + */ + /** + * Resolves an NPM module for a given `specifier` and an optional `origin`. + * @param {string|URL} specifier + * @param {string|URL=} [origin] + * @param {{ prefix?: string, type?: 'commonjs' | 'module' }} [options] + * @return {ModuleResolution|null} + */ + export function resolve(specifier: string | URL, origin?: (string | URL) | undefined, options?: { + prefix?: string; + type?: "commonjs" | "module"; + }): ModuleResolution | null; + namespace _default { + export { resolve }; + } + export default _default; + export type ModuleResolution = { + package: Package; + origin: string; + type: "commonjs" | "module"; + url: string; + }; + import { Package } from "commonjs/package"; +} +declare module "npm/service-worker" { + /** + * @ignore + * @param {Request} + * @param {object} env + * @param {import('../service-worker/context.js').Context} ctx + * @return {Promise} + */ + export function onRequest(request: any, env: object, ctx: import("service-worker/context").Context): Promise; + /** + * Handles incoming 'npm:///' requests. + * @param {Request} request + * @param {object} env + * @param {import('../service-worker/context.js').Context} ctx + * @return {Response?} + */ + export default function _default(request: Request, env: object, ctx: import("service-worker/context").Context): Response | null; +} +declare module "service-worker/global" { + export class ServiceWorkerGlobalScope { + get isServiceWorkerScope(): boolean; + get ExtendableEvent(): typeof ExtendableEvent; + get FetchEvent(): typeof FetchEvent; + get serviceWorker(): any; + set registration(value: any); + get registration(): any; + get clients(): import("service-worker/clients").Clients; + set onactivate(listener: any); + get onactivate(): any; + set onmessage(listener: any); + get onmessage(): any; + set oninstall(listener: any); + get oninstall(): any; + set onfetch(listener: any); + get onfetch(): any; + skipWaiting(): Promise; + } + const _default: ServiceWorkerGlobalScope; + export default _default; + import { ExtendableEvent } from "service-worker/events"; + import { FetchEvent } from "service-worker/events"; +} +declare module "service-worker/init" { + export function onRegister(event: any): Promise; + export function onUnregister(event: any): Promise; + export function onSkipWaiting(event: any): Promise; + export function onActivate(event: any): Promise; + export function onFetch(event: any): Promise; + export function onShowNotification(event: any, target: any): any; + export function onNotificationClose(event: any, target: any): void; + export function onGetNotifications(event: any, target: any): any; + export const workers: Map; + export const channel: ipc.IPCBroadcastChannel; + export class ServiceWorkerInstance extends Worker { + constructor(filename: any, options: any); + get info(): any; + get notifications(): any[]; + onMessage(event: any): Promise; + #private; + } + export class ServiceWorkerInfo { + constructor(data: any); + id: any; + url: any; + hash: any; + scope: any; + scheme: any; + scriptURL: any; + serializedWorkerArgs: any; + get pathname(): string; + } + const _default: any; + export default _default; + import ipc from "ipc"; +} +declare module "service-worker/main" { + export {}; +} +declare function isTypedArray(object: any): boolean; +declare function isTypedArray(object: any): boolean; +declare function isArrayBuffer(object: any): object is ArrayBuffer; +declare function isArrayBuffer(object: any): object is ArrayBuffer; +declare function findMessageTransfers(transfers: any, object: any, options?: any): any; +declare function findMessageTransfers(transfers: any, object: any, options?: any): any; +declare const Uint8ArrayPrototype: Uint8Array; +declare const TypedArrayPrototype: any; +declare const TypedArray: any; +declare const ports: any[]; +declare module "service-worker/storage" { + /** + * A factory for creating storage interfaces. + * @param {'memoryStorage'|'localStorage'|'sessionStorage'} type + * @return {Promise} + */ + export function createStorageInterface(type: "memoryStorage" | "localStorage" | "sessionStorage"): Promise; + /** + * @typedef {{ done: boolean, value: string | undefined }} IndexIteratorResult + */ + /** + * An iterator interface for an `Index` instance. + */ + export class IndexIterator { + /** + * `IndexIterator` class constructor. + * @ignore + * @param {Index} index + */ + constructor(index: Index); + /** + * `true` if the iterator is "done", otherwise `false`. + * @type {boolean} + */ + get done(): boolean; + /** + * Returns the next `IndexIteratorResult`. + * @return {IndexIteratorResult} + */ + next(): IndexIteratorResult; + /** + * Mark `IndexIterator` as "done" + * @return {IndexIteratorResult} + */ + return(): IndexIteratorResult; + #private; + } + /** + * A container used by the `Provider` to index keys and values + */ + export class Index { + /** + * A reference to the keys in this index. + * @type {string[]} + */ + get keys(): string[]; + /** + * A reference to the values in this index. + * @type {string[]} + */ + get values(): string[]; + /** + * The number of entries in this index. + * @type {number} + */ + get length(): number; + /** + * Returns the key at a given `index`, if it exists otherwise `null`. + * @param {number} index} + * @return {string?} + */ + key(index: number): string | null; + /** + * Returns the value at a given `index`, if it exists otherwise `null`. + * @param {number} index} + * @return {string?} + */ + value(index: number): string | null; + /** + * Inserts a value in the index. + * @param {string} key + * @param {string} value + */ + insert(key: string, value: string): void; + /** + * Computes the index of a key in this index. + * @param {string} key + * @return {number} + */ + indexOf(key: string): number; + /** + * Clears all keys and values in the index. + */ + clear(): void; + /** + * Returns an entry at `index` if it exists, otherwise `null`. + * @param {number} index + * @return {string[]|null} + */ + entry(index: number): string[] | null; + /** + * Removes entries at a given `index`. + * @param {number} index + * @return {boolean} + */ + remove(index: number): boolean; + /** + * Returns an array of computed entries in this index. + * @return {IndexIterator} + */ + entries(): IndexIterator; + /** + * @ignore + * @return {IndexIterator} + */ + [Symbol.iterator](): IndexIterator; + #private; + } + /** + * A base class for a storage provider. + */ + export class Provider { + /** + * An error currently associated with the provider, likely from an + * async operation. + * @type {Error?} + */ + get error(): Error; + /** + * A promise that resolves when the provider is ready. + * @type {Promise} + */ + get ready(): Promise; + /** + * A reference the service worker storage ID, which is the service worker + * registration ID. + * @type {string} + * @throws DOMException + */ + get id(): string; + /** + * A reference to the provider `Index` + * @type {Index} + * @throws DOMException + */ + get index(): Index; + /** + * The number of entries in the provider. + * @type {number} + * @throws DOMException + */ + get length(): number; + /** + * Returns `true` if the provider has a value for a given `key`. + * @param {string} key} + * @return {boolean} + * @throws DOMException + */ + has(key: string): boolean; + /** + * Get a value by `key`. + * @param {string} key + * @return {string?} + * @throws DOMException + */ + get(key: string): string | null; + /** + * Sets a `value` by `key` + * @param {string} key + * @param {string} value + * @throws DOMException + */ + set(key: string, value: string): void; + /** + * Removes a value by `key`. + * @param {string} key + * @return {boolean} + * @throws DOMException + */ + remove(key: string): boolean; + /** + * Clear all keys and values. + * @throws DOMException + */ + clear(): void; + /** + * The keys in the provider index. + * @return {string[]} + * @throws DOMException + */ + keys(): string[]; + /** + * The values in the provider index. + * @return {string[]} + * @throws DOMException + */ + values(): string[]; + /** + * Returns the key at a given `index` + * @param {number} index + * @return {string|null} + * @throws DOMException + */ + key(index: number): string | null; + /** + * Loads the internal index with keys and values. + * @return {Promise} + */ + load(): Promise; + #private; + } + /** + * An in-memory storage provider. It just used the built-in provider `Index` + * for storing key-value entries. + */ + export class MemoryStorageProvider extends Provider { + } + /** + * A session storage provider that persists for the runtime of the + * application and through service worker restarts. + */ + export class SessionStorageProvider extends Provider { + /** + * Remove a value by `key`. + * @param {string} key + * @return {string?} + * @throws DOMException + * @throws NotFoundError + */ + remove(key: string): string | null; + } + /** + * A local storage provider that persists until the data is cleared. + */ + export class LocalStorageProvider extends Provider { + } + /** + * A generic interface for storage implementations + */ + export class Storage { + /** + * A factory for creating a `Storage` instance that is backed + * by a storage provider. Extending classes should define a `Provider` + * class that is statically available on the extended `Storage` class. + * @param {symbol} token + * @return {Promise>} + */ + static create(token: symbol): Promise; + /** + * `Storage` class constructor. + * @ignore + * @param {symbol} token + * @param {Provider} provider + */ + constructor(token: symbol, provider: Provider); + /** + * A readonly reference to the storage provider. + * @type {Provider} + */ + get provider(): Provider; + /** + * The number of entries in the storage. + * @type {number} + */ + get length(): number; + /** + * Returns `true` if the storage has a value for a given `key`. + * @param {string} key + * @return {boolean} + * @throws TypeError + */ + hasItem(key: string, ...args: any[]): boolean; + /** + * Clears the storage of all entries + */ + clear(): void; + /** + * Returns the key at a given `index` + * @param {number} index + * @return {string|null} + */ + key(index: number, ...args: any[]): string | null; + /** + * Get a storage value item for a given `key`. + * @param {string} key + * @return {string|null} + */ + getItem(key: string, ...args: any[]): string | null; + /** + * Removes a storage value entry for a given `key`. + * @param {string} + * @return {boolean} + */ + removeItem(key: any, ...args: any[]): boolean; + /** + * Sets a storage item `value` for a given `key`. + * @param {string} key + * @param {string} value + */ + setItem(key: string, value: string, ...args: any[]): void; + /** + * @ignore + */ + get [Symbol.toStringTag](): string; + #private; + } + /** + * An in-memory `Storage` interface. + */ + export class MemoryStorage extends Storage { + static Provider: typeof MemoryStorageProvider; + } + /** + * A locally persisted `Storage` interface. + */ + export class LocalStorage extends Storage { + static Provider: typeof LocalStorageProvider; + } + /** + * A session `Storage` interface. + */ + export class SessionStorage extends Storage { + static Provider: typeof SessionStorageProvider; + } + namespace _default { + export { Storage }; + export { LocalStorage }; + export { MemoryStorage }; + export { SessionStorage }; + export { createStorageInterface }; + } + export default _default; + export type IndexIteratorResult = { + done: boolean; + value: string | undefined; + }; +} +declare module "service-worker/worker" { + export function onReady(): void; + export function onMessage(event: any): Promise; + const _default: any; + export default _default; + export namespace SERVICE_WORKER_READY_TOKEN { + let __service_worker_ready: boolean; + } + export namespace module { + let exports: {}; + } + export const events: Set; + export namespace stages { + let register: Deferred; + let install: Deferred; + let activate: Deferred; + } + import { Deferred } from "async"; +} +declare module "shared-worker/debug" { + export function debug(...args: any[]): void; + export default debug; +} +declare module "shared-worker/global" { + export class SharedWorkerGlobalScope { + get isSharedWorkerScope(): boolean; + set onconnect(listener: any); + get onconnect(): any; + } + const _default: SharedWorkerGlobalScope; + export default _default; +} +declare module "shared-worker/init" { + export function onInstall(event: any): Promise; + export function onUninstall(event: any): Promise; + export function onConnect(event: any): Promise; + export const workers: Map; + export { channel }; + export class SharedWorkerInstance extends Worker { + constructor(filename: any, options: any); + get info(): any; + onMessage(event: any): Promise; + #private; + } + export class SharedWorkerInfo { + constructor(data: any); + id: any; + port: any; + client: any; + scriptURL: any; + url: any; + hash: any; + get pathname(): string; + } + const _default: any; + export default _default; + import { channel } from "shared-worker/index"; +} +declare module "shared-worker/main" { + export {}; +} +declare module "shared-worker/state" { + export const state: any; + export default state; +} +declare module "shared-worker/worker" { + export function onReady(): void; + export function onMessage(event: any): Promise; + const _default: any; + export default _default; + export namespace SHARED_WORKER_READY_TOKEN { + let __shared_worker_ready: boolean; + } + export namespace module { + let exports: {}; + } + export const connections: Set; +} +declare module "test/harness" { + /** + * @typedef {import('./index').Test} Test + * @typedef {(t: Test) => Promise | void} TestCase + * @typedef {{ + * bootstrap(): Promise + * close(): Promise + * }} Harness + */ + /** + * @template {Harness} T + * @typedef {{ + * ( + * name: string, + * cb?: (harness: T, test: Test) => (void | Promise) + * ): void; + * ( + * name: string, + * opts: object, + * cb: (harness: T, test: Test) => (void | Promise) + * ): void; + * only( + * name: string, + * cb?: (harness: T, test: Test) => (void | Promise) + * ): void; + * only( + * name: string, + * opts: object, + * cb: (harness: T, test: Test) => (void | Promise) + * ): void; + * skip( + * name: string, + * cb?: (harness: T, test: Test) => (void | Promise) + * ): void; + * skip( + * name: string, + * opts: object, + * cb: (harness: T, test: Test) => (void | Promise) + * ): void; + * }} TapeTestFn + */ + /** + * @template {Harness} T + * @param {import('./index.js')} tapzero + * @param {new (options: object) => T} harnessClass + * @returns {TapeTestFn} + */ + export function wrapHarness(tapzero: typeof import("test/index"), harnessClass: new (options: object) => T): TapeTestFn; + export default exports; + /** + * @template {Harness} T + */ + export class TapeHarness { + /** + * @param {import('./index.js')} tapzero + * @param {new (options: object) => T} harnessClass + */ + constructor(tapzero: typeof import("test/index"), harnessClass: new (options: object) => T); + /** @type {import('./index.js')} */ + tapzero: typeof import("test/index"); + /** @type {new (options: object) => T} */ + harnessClass: new (options: object) => T; + /** + * @param {string} testName + * @param {object} [options] + * @param {(harness: T, test: Test) => (void | Promise)} [fn] + * @returns {void} + */ + test(testName: string, options?: object, fn?: (harness: T, test: Test) => (void | Promise)): void; + /** + * @param {string} testName + * @param {object} [options] + * @param {(harness: T, test: Test) => (void | Promise)} [fn] + * @returns {void} + */ + only(testName: string, options?: object, fn?: (harness: T, test: Test) => (void | Promise)): void; + /** + * @param {string} testName + * @param {object} [options] + * @param {(harness: T, test: Test) => (void | Promise)} [fn] + * @returns {void} + */ + skip(testName: string, options?: object, fn?: (harness: T, test: Test) => (void | Promise)): void; + /** + * @param {(str: string, fn?: TestCase) => void} tapzeroFn + * @param {string} testName + * @param {object} [options] + * @param {(harness: T, test: Test) => (void | Promise)} [fn] + * @returns {void} + */ + _test(tapzeroFn: (str: string, fn?: TestCase) => void, testName: string, options?: object, fn?: (harness: T, test: Test) => (void | Promise)): void; + /** + * @param {Test} assert + * @param {object} options + * @param {(harness: T, test: Test) => (void | Promise)} fn + * @returns {Promise} + */ + _onAssert(assert: Test, options: object, fn: (harness: T, test: Test) => (void | Promise)): Promise; + } + export type Test = import("test/index").Test; + export type TestCase = (t: Test) => Promise | void; + export type Harness = { + bootstrap(): Promise; + close(): Promise; + }; + export type TapeTestFn = { + (name: string, cb?: (harness: T, test: Test) => (void | Promise)): void; + (name: string, opts: object, cb: (harness: T, test: Test) => (void | Promise)): void; + only(name: string, cb?: (harness: T, test: Test) => (void | Promise)): void; + only(name: string, opts: object, cb: (harness: T, test: Test) => (void | Promise)): void; + skip(name: string, cb?: (harness: T, test: Test) => (void | Promise)): void; + skip(name: string, opts: object, cb: (harness: T, test: Test) => (void | Promise)): void; + }; + import * as exports from "test/harness"; + namespace ___home_werle_repos_socketsupply_socket_api_test_harness_ { } +} +declare module "vm/init" { + export {}; +} +declare function isTypedArray(object: any): boolean; +declare function isTypedArray(object: any): boolean; +declare function isArrayBuffer(object: any): object is ArrayBuffer; +declare function isArrayBuffer(object: any): object is ArrayBuffer; +declare function findMessageTransfers(transfers: any, object: any, options?: any): any; +declare function findMessageTransfers(transfers: any, object: any, options?: any): any; +declare const Uint8ArrayPrototype: Uint8Array; +declare const TypedArrayPrototype: any; +declare const TypedArray: any; +declare class Client extends EventTarget { + constructor(id: any, port: any); + id: any; + port: any; + onMessage(event: any): void; + postMessage(...args: any[]): any; +} +declare class Realm { + constructor(state: any, port: any); + /** + * The `MessagePort` for the VM realm. + * @type {MessagePort} + */ + port: MessagePort; + /** + * A reference to the top level worker statae + * @type {State} + */ + state: State; + /** + * Known content worlds that exist in a realm + * @type {Map} + */ + worlds: Map; + get clients(): Map; + postMessage(...args: any[]): void; +} +declare class State { + static init(): State; + /** + * All known connected `MessagePort` instances + * @type {MessagePort[]} + */ + ports: MessagePort[]; + /** + * Pending events to be dispatched to realm + * @type {MessageEvent[]} + */ + pending: MessageEvent[]; + /** + * The realm for all virtual machines. This is a headless webview + */ + realm: any; + clients: Map; + onConnect(event: any): void; + init(): void; + onPortMessage(port: any, event: any): void; +} +declare module "vm/world" { + export {}; +} diff --git a/api/internal/conduit.js b/api/internal/conduit.js index 791cc7f6e6..8f05218c6a 100644 --- a/api/internal/conduit.js +++ b/api/internal/conduit.js @@ -22,7 +22,7 @@ let defaultConduitPort = globalThis.__args.conduit?.port || 0 * @typedef {{ options: object, payload: Uint8Array }} ReceiveMessage * @typedef {function(Error?, ReceiveCallback | undefined)} ReceiveCallback * @typedef {{ isActive: boolean, handles: { ids: string[], count: number }}} ConduitDiagnostics - * @typedef {{ isActive: boolean, port: number }} ConduitStatus + * @typedef {{ isActive: boolean, port: number, sharedKey: string }} ConduitStatus * @typedef {{ * id?: string|BigInt|number, * sharedKey?: string @@ -110,7 +110,8 @@ export class Conduit extends EventTarget { return { port: result.data.port || 0, - isActive: result.data.isActive || false + isActive: result.data.isActive || false, + sharedKey: result.data.sharedKey } } @@ -133,6 +134,37 @@ export class Conduit extends EventTarget { } } + /** + * Gets the current conduit shared key. + * @return {Promise} + */ + static async getSharedKey () { + const result = await ipc.request('internal.conduit.getSharedKey') + + if (result.err) { + throw result.err + } + + return result.data.sharedKey + } + + /** + * Sets the conduit shared key. + * @param {string} sharedKey + * @return {Promise} + */ + static async setSharedKey (sharedKey) { + const result = await ipc.request('internal.conduit.setSharedKey', { + sharedKey + }) + + if (result.err) { + throw result.err + } + + return result.data.sharedKey + } + /** * @type {boolean} */ @@ -159,9 +191,9 @@ export class Conduit extends EventTarget { port = 0 /** - * @type {number?} + * @type {string} */ - id = null + id = '0' /** * @type {string} @@ -199,7 +231,7 @@ export class Conduit extends EventTarget { constructor (options) { super() - this.id = options.id + this.id = String(options.id || '0') this.sharedKey = options.sharedKey || this.sharedKey // @ts-ignore this.port = this.constructor.port diff --git a/api/internal/database.js b/api/internal/database.js index 316ceb4c4f..140987c7bb 100644 --- a/api/internal/database.js +++ b/api/internal/database.js @@ -895,7 +895,6 @@ export class Database extends EventTarget { /** * Gets a "readonly" value by `key` in the `Database` object storage. - * @param {string} key * @param {?DatabaseEntriesOptions|undefined} [options] * @return {Promise} */ diff --git a/api/internal/globals.js b/api/internal/globals.js index ccaecd8a1f..1b863ed6e4 100644 --- a/api/internal/globals.js +++ b/api/internal/globals.js @@ -23,16 +23,28 @@ export class GlobalsRegistry { } } -const registry = ( - globalThis.top?.__globals ?? - globalThis.__globals ?? - new GlobalsRegistry() -) - +const registry = getTopRegistry() const RuntimeReadyPromiseResolvers = Promise.withResolvers() + registry.register('RuntimeReadyPromiseResolvers', RuntimeReadyPromiseResolvers) registry.register('RuntimeReadyPromise', RuntimeReadyPromiseResolvers.promise) +function getTopRegistry () { + try { + if (globalThis.top?.__globals) { + return globalThis.top?.__globals + } + } catch (err) {} + + try { + if (globalThis.__globals) { + return globalThis.__globals + } + } catch (err) {} + + return new GlobalsRegistry() +} + /** * Gets a runtime global value by name. * @ignore diff --git a/api/internal/init.js b/api/internal/init.js index 27fff0b5e8..684a544c58 100644 --- a/api/internal/init.js +++ b/api/internal/init.js @@ -301,7 +301,7 @@ class RuntimeWorker extends GlobalWorker { * Internal worker pool * @ignore */ - static pool = globalThis.top?.Worker?.pool ?? new Map() + static pool = null /** * Handles `Symbol.species` @@ -314,6 +314,7 @@ class RuntimeWorker extends GlobalWorker { #id = null #objectURL = null #onglobaldata = null + #onbroadcastchannelmessage = null /** * `RuntimeWorker` class worker. @@ -345,9 +346,12 @@ class RuntimeWorker extends GlobalWorker { const url = encodeURIComponent(new URL(filename, location.href).toString()) const id = String(rand64()) - const topClient = globalThis.__args.client.top || globalThis.__args.client + let topClient = globalThis.__args.client + try { + topClient = globalThis.__args.client.top || globalThis.__args.client + } catch (err) {} - const __args = { ...globalThis.__args, client: {} } + const __args = { ...globalThis.__args, ...options?.args, client: {} } const preload = ` Object.defineProperty(globalThis, '__args', { configurable: false, @@ -466,7 +470,17 @@ class RuntimeWorker extends GlobalWorker { }, [data]) } + this.#onbroadcastchannelmessage = (event) => { + this.postMessage({ + __runtime_worker_event: { + type: 'broadcastchannelmessage', + detail: event.detail + } + }) + } + globalThis.addEventListener('data', this.#onglobaldata) + globalThis.addEventListener('broadcastchannelmessage', this.#onbroadcastchannelmessage) const postMessage = this.postMessage.bind(this) const addEventListener = this.addEventListener.bind(this) @@ -575,10 +589,17 @@ class RuntimeWorker extends GlobalWorker { terminate () { globalThis.removeEventListener('data', this.#onglobaldata) + globalThis.removeEventListener('broadcastchannelmessage', this.#onbroadcastchannelmessage) return super.terminate() } } +try { + RuntimeWorker.pool = globalThis.top?.Worker?.pool || new Map() +} catch (err) { + RuntimeWorker.pool = new Map() +} + // patch `globalThis.Worker` if (globalThis.Worker === GlobalWorker) { globalThis.Worker = RuntimeWorker @@ -817,7 +838,7 @@ class ConcurrentQueue extends EventTarget { } } -class RuntimeXHRPostQueue extends ConcurrentQueue { +class RuntimeQueuedResponses extends ConcurrentQueue { async dispatch (id, seq, params, headers, options = null) { if (options?.workerId) { if (RuntimeWorker.pool.has(options.workerId)) { @@ -825,7 +846,7 @@ class RuntimeXHRPostQueue extends ConcurrentQueue { if (worker) { worker.postMessage({ __runtime_worker_event: { - type: 'runtime-xhr-post-queue', + type: 'runtime-queued-response', detail: { id, seq, params, headers } @@ -978,14 +999,60 @@ hooks.onReady(async () => { } catch (err) { console.error(err.stack || err) } + + if (globalThis.window && globalThis.window !== globalThis.top) { + globalThis.addEventListener('message', (e) => { + if (e.data?.__runtime_frame_data) { + globalThis.dispatchEvent(new CustomEvent('data', { + detail: e.data?.__runtime_frame_data + })) + } else if (e.data?.__runtime_frame_broadcast_channel_message) { + globalThis.dispatchEvent(new CustomEvent('broadcastchannelmessage', { + detail: e.data?.__runtime_frame_broadcast_channel_message + })) + } + }) + } + + if (globalThis.window && globalThis.__RUNTIME_SHOULD_NOT_PROXY_DATA_TO_FRAMES__ !== true) { + globalThis.addEventListener('data', (e) => { + if (globalThis.frames.length) { + for (let i = 0; i < globalThis.frames.length; ++i) { + try { + globalThis.frames[i].postMessage({ + __runtime_frame_data: e.detail + }, '*') + } catch (err) { + console.error(err) + } + } + } + }) + } + + if (globalThis.window && globalThis.__RUNTIME_SHOULD_NOT_PROXY_BROADCAST_CHANNEL_MESSAGES_TO_FRAMES__ !== true) { + globalThis.addEventListener('broadcastchannelmessage', (e) => { + if (globalThis.frames.length) { + for (let i = 0; i < globalThis.frames.length; ++i) { + try { + globalThis.frames[i].postMessage({ + __runtime_frame_broadcast_channel_message: e.detail + }, '*') + } catch (err) { + console.error(err) + } + } + } + }) + } }) // symbolic globals -globals.register('RuntimeXHRPostQueue', new RuntimeXHRPostQueue()) +globals.register('RuntimeQueuedResponses', new RuntimeQueuedResponses()) globals.register('RuntimeExecution', new asyncHooks.CoreAsyncResource('RuntimeExecution')) // prevent further construction if this class is indirectly referenced -RuntimeXHRPostQueue.prototype.constructor = IllegalConstructor +RuntimeQueuedResponses.prototype.constructor = IllegalConstructor Object.defineProperty(globalThis, '__globals', { enumerable: false, configurable: false, diff --git a/api/internal/worker.js b/api/internal/worker.js index dcada6ccf7..9356fc76f3 100644 --- a/api/internal/worker.js +++ b/api/internal/worker.js @@ -66,14 +66,14 @@ export const self = globalThis.self || globalThis if (isWorkerScope) { // handle worker messages that are eventually propgated to `workerGlobalScopeEventTarget` globalThis.addEventListener('message', onWorkerMessage) - globalThis.addEventListener('runtime-xhr-post-queue', function onXHRPostQueue (event) { + globalThis.addEventListener('runtime-queued-response', function onRuntimeQueuedResponse (event) { if (isClosed) { - globalThis.removeEventListener('runtime-xhr-post-queue', onXHRPostQueue) + globalThis.removeEventListener('runtime-queued-response', onRuntimeQueuedResponse) return false } const { id, seq, params } = event.detail || {} - globals.get('RuntimeXHRPostQueue').dispatch( + globals.get('RuntimeQueuedResponses').dispatch( id, seq, params diff --git a/api/ipc.js b/api/ipc.js index e95cb270fe..a23252f64d 100644 --- a/api/ipc.js +++ b/api/ipc.js @@ -33,7 +33,7 @@ * ``` */ -/* global webkit, chrome, external, reportError */ +/* global webkit, chrome, external, reportError, ErrorEvent */ import { AbortError, InternalError, @@ -1479,10 +1479,10 @@ export async function write (command, value, buffer, options) { * with buffered bytes. * @param {string} command * @param {any=} value - * @param {object=} options + * @param {{ timeout?: number, responseType?: string, signal?: AbortSignal, cache?: boolean}=} [options] * @ignore */ -export async function request (command, value, options) { +export async function request (command, value, options = null) { if (!globalThis.XMLHttpRequest) { const err = new Error('XMLHttpRequest is not supported in environment') return Result.from(err) @@ -1746,6 +1746,8 @@ export function inflateIPCMessageTransfers (object, types = new Map()) { if (object.__type__ === 'IPCMessagePort' && object.id) { return IPCMessagePort.create(object) + } else if (object.__type__ === 'Buffer' && object.data) { + return Buffer.from(object.data, 'base64') } else { for (const key in object) { const value = object[key] @@ -1760,8 +1762,10 @@ export function inflateIPCMessageTransfers (object, types = new Map()) { export function findIPCMessageTransfers (transfers, object) { if (ArrayBuffer.isView(object)) { add(object.buffer) + return { __type__: 'Buffer', data: Buffer.from(object).toString('base64') } } else if (object instanceof ArrayBuffer) { add(object) + return { __type__: 'Buffer', data: Buffer.from(object).toString('base64') } } else if (Array.isArray(object)) { for (let i = 0; i < object.length; ++i) { object[i] = findIPCMessageTransfers(transfers, object[i]) @@ -1774,17 +1778,6 @@ export function findIPCMessageTransfers (transfers, object) { ) ) { const port = IPCMessagePort.create(object) - object.addEventListener('message', function onMessage (event) { - if (port.closed === true) { - port.onmessage = null - event.preventDefault() - event.stopImmediatePropagation() - object.removeEventListener('message', onMessage) - return false - } - - port.dispatchEvent(new MessageEvent('message', event)) - }) port.onmessage = (event) => { if (port.closed === true) { @@ -1804,7 +1797,10 @@ export function findIPCMessageTransfers (transfers, object) { return port } else { for (const key in object) { - object[key] = findIPCMessageTransfers(transfers, object[key]) + object[key] = findIPCMessageTransfers( + transfers, + object[key] + ) } } } @@ -1836,7 +1832,7 @@ export class IPCMessagePort extends MessagePort { const channel = typeof options?.channel === 'string' ? new BroadcastChannel(options.channel) : ( - (options?.channel && new BroadcastChannel(options.channel.name)) ?? + (options?.channel && new BroadcastChannel(options.channel.name, options.channel)) ?? new BroadcastChannel(id) ) @@ -2042,9 +2038,10 @@ export class IPCMessagePort extends MessagePort { } [Symbol.for('socket.runtime.serialize')] () { + const channel = ports.get(this.id)?.channel return { __type__: 'IPCMessagePort', - channel: ports.get(this.id)?.channel?.name ?? null, + channel: channel ? { name: channel.name, origin: channel.origin || location.origin } : null, id: this.id } } @@ -2070,20 +2067,19 @@ export class IPCMessageChannel extends MessageChannel { constructor (options = null) { super() this.#id = String(options?.id ?? rand64()) - this.#channel = options?.channel ?? new BroadcastChannel(this.#id) + this.#channel = options?.channel ?? new IPCBroadcastChannel(this.#id) this.#port1 = IPCMessagePort.create(options?.port1) this.#port2 = IPCMessagePort.create(options?.port2) this.port1[Symbol.for('socket.runtime.ipc.MessagePort.handlePostMessage')] = (message, options) => { - this.port2.channel.postMessage(message, options) + this.port2.postMessage(message, options) return false } - this.port2[Symbol.for('socket.runtime.ipc.MessagePort.handlePostMessage')] = (message, options) => { - this.port2.channel.postMessage(message, options) - return false - } + this.port2.addEventListener('message', (e) => { + this.port1.postMessage(e.data) + }) } get id () { @@ -2103,6 +2099,250 @@ export class IPCMessageChannel extends MessageChannel { } } +export class IPCBroadcastChannel extends EventTarget { + static subscriptions = new Map() + + /** + * @type {string} + */ + #name + #token = Math.random().toString(16).slice(2) + #pending = Promise.resolve() + + /** + * @type {string} + */ + #targetOrigin = location.origin + + /** + * @type {(function(CustomEvent):any)|null} + */ + #listener = null + + /** + * @type {{ id: string }|null} + */ + #subscription = null + + /** + * @type {function(ErrorEvent):any|null} + */ + #onerror + + /** + * @type {function(MessageEvent):any|null} + */ + #onmessage + + /** + * @type {function(ErrorEvent):any|null} + */ + #onmessageerror + + /** + * @param {string} name + * @param {{ origin?: string }=} [options] + */ + constructor (name, options = null) { + super() + this.#name = name + this.#targetOrigin = options?.origin || this.#targetOrigin + } + + get name () { + return this.#name + } + + get origin () { + return this.#subscription?.origin ?? (this.#targetOrigin || location.origin) + } + + get key () { + return new URL(this.name, this.origin).href + } + + get token () { + return this.#token + } + + /** + * @type {function(MessageEvent):any|null} + */ + get onmessage () { return this.#onmessage ?? null } + set onmessage (onmessage) { + if (typeof this.#onmessage === 'function') { + this.removeEventListener('message', this.#onmessage) + } + + this.#onmessage = null + + if (typeof onmessage === 'function') { + this.#onmessage = onmessage + this.addEventListener('message', this.#onmessage) + } + } + + /** + * @type {function(ErrorEvent):any|null} + */ + get onmessageerror () { return this.#onmessageerror ?? null } + set onmessageerror (onmessageerror) { + if (typeof this.#onmessageerror === 'function') { + this.removeEventListener('messageerror', this.#onmessageerror) + } + + this.#onmessageerror = null + + if (typeof onmessageerror === 'function') { + this.#onmessageerror = onmessageerror + this.addEventListener('messageerror', this.#onmessageerror) + } + } + + /** + * @type {function(ErrorEvent):any|null} + */ + get onerror () { return this.#onerror ?? null } + set onerror (onerror) { + if (typeof this.#onerror === 'function') { + this.removeEventListener('error', this.#onerror) + } + + this.#onerror = null + + if (typeof onerror === 'function') { + this.#onerror = onerror + this.addEventListener('error', this.#onerror) + } + } + + async startMessages () { + if (this.#subscription) { + return + } + + if (IPCBroadcastChannel.subscriptions.has(this.key)) { + this.#subscription = await IPCBroadcastChannel.subscriptions.get(this.key) + if (this.#subscription) { + this.#subscription.refs++ + } + } + + if (!this.#subscription) { + const promise = request('broadcast_channel.subscribe', { + origin: this.#targetOrigin, + name: this.name + }) + + IPCBroadcastChannel.subscriptions.set( + this.key, + promise.then((result) => { + if (result.err) { + return Promise.reject(result.err) + } + + return result.data + }) + ) + + this.#subscription = await IPCBroadcastChannel.subscriptions.get(this.key) + this.#subscription.refs = 1 + } + + if (this.#subscription) { + this.#listener = async (/** @type {CustomEvent} */ event) => { + if ( + event.detail?.data?.token !== this.#token && + event.detail?.data?.subscription?.name === this.name && + event.detail?.source === 'broadcast_channel.subscribe' && + ( + event.detail?.data?.origin === '*' || + event.detail?.data?.origin === this.origin + ) + ) { + this.dispatchEvent(new MessageEvent('message', { + data: inflateIPCMessageTransfers(event.detail.data.message.data) + })) + } + } + + globalThis.addEventListener('broadcastchannelmessage', this.#listener) + } + + gc.ref(this) + } + + /** + * @overload + * @param {'message'} type + * @param {function(MessageEvent):any} callback + * @param {{ once?: boolean }=} [options] + * + * @overload + * @param {'messageerror'} type + * @param {function(ErrorEvent):any} callback + * @param {{ once?: boolean }=} [options] + */ + addEventListener (type, callback, options = undefined) { + super.addEventListener(type, callback, options) + if (type === 'message') { + this.startMessages().catch((error) => { + this.dispatchEvent(new ErrorEvent('error', { error })) + }) + } + } + + postMessage (message, optionsOrTransferList) { + const options = { origin: globalThis.origin, transfer: [] } + + if (Array.isArray(optionsOrTransferList)) { + options.transfer.push(...optionsOrTransferList) + } else if (Array.isArray(optionsOrTransferList?.transfer)) { + options.transfer.push(...optionsOrTransferList.transfer) + } + + if (typeof optionsOrTransferList?.origin === 'string') { + options.origin = optionsOrTransferList.origin + } + + const transfers = new Set(options.transfer) + const serializedMessage = serialize(findIPCMessageTransfers(transfers, message)) + this.#pending = this.#pending.then(async () => { + const result = await request('broadcast_channel.postMessage', { + origin: options.origin, + token: this.#token, + name: this.name, + data: JSON.stringify(serializedMessage) + }) + + if (result.err) { + this.dispatchEvent(new ErrorEvent('messageerror', { + error: result.err + })) + } + + return result.data + }) + + return this.#pending + } + + [Symbol.for('socket.runtime.gc.finalizer')] () { + return { + args: [this.#subscription, this.#listener], + async handle (subscription, listener) { + if (subscription?.id && --subscription.refs === 0) { + await request('broadcast_channel.unsubscribe', { id: subscription.id }) + } + + if (listener) { + globalThis.removeEventListener('broadcastchannelmessage', listener) + } + } + } + } +} + // eslint-disable-next-line import * as exports from './ipc.js' export default exports diff --git a/api/location.js b/api/location.js index baebeaaff7..b76dbdb1f0 100644 --- a/api/location.js +++ b/api/location.js @@ -29,6 +29,8 @@ export class Location { url = new URL(globalThis.location.href) } else if (globalThis.__args.client.host === globalThis.location.hostname) { url = new URL(globalThis.location.href) + } else if (globalThis.top !== globalThis) { + return new URL(globalThis.location.href) } if (!url || url.hostname !== globalThis.__args?.config?.meta_bundle_identifier) { diff --git a/api/mime/index.js b/api/mime/index.js index 7131bce244..81dc241d7f 100644 --- a/api/mime/index.js +++ b/api/mime/index.js @@ -1,4 +1,6 @@ /* global XMLHttpRequest */ +import { MIMEParams } from './params.js' +import { MIMEType } from './type.js' import ipc from '../ipc.js' /** @@ -318,86 +320,6 @@ export async function lookupSync (query) { return results } -export class MIMEParams extends Map { - toString () { - return Array - .from(this.entries()) - .map((entry) => entry.join('=')) - .join(';') - } -} - -export class MIMEType { - #type = null - #params = null - #subtype = null - - constructor (input) { - input = String(input) - - const args = input.split(';') - const mime = args.shift() - const types = mime.split('/') - - if (types.length !== 2 || !types[0] || !types[1]) { - throw new TypeError(`Invalid MIMEType input given: ${mime}`) - } - - const [type, subtype] = types - - this.#type = type.toLowerCase() - // @ts-ignore - this.#params = new MIMEParams(args.map((a) => a.trim().split('=').map((v) => v.trim()))) - this.#subtype = subtype - } - - get type () { - return this.#type - } - - set type (value) { - if (!value || typeof value !== 'string') { - throw new TypeError('MIMEType type must be a string') - } - - this.#type = value - } - - get subtype () { - return this.#subtype - } - - set subtype (value) { - if (!value || typeof value !== 'string') { - throw new TypeError('MIMEType subtype must be a string') - } - - this.#subtype = value - } - - get essence () { - return `${this.type}/${this.subtype}` - } - - get params () { - return this.#params - } - - toString () { - const params = this.params.toString() - - if (params) { - return `${this.essence};${params}` - } - - return this.essence - } - - toJSON () { - return this.toString() - } -} - export default { // API Database, diff --git a/api/mime/params.js b/api/mime/params.js new file mode 100644 index 0000000000..d33fdab6b5 --- /dev/null +++ b/api/mime/params.js @@ -0,0 +1,10 @@ +export class MIMEParams extends Map { + toString () { + return Array + .from(this.entries()) + .map((entry) => entry.join('=')) + .join(';') + } +} + +export default MIMEParams diff --git a/api/mime/type.js b/api/mime/type.js new file mode 100644 index 0000000000..195b954df1 --- /dev/null +++ b/api/mime/type.js @@ -0,0 +1,74 @@ +import { MIMEParams } from './params.js' + +export class MIMEType { + #type = null + #params = null + #subtype = null + + constructor (input) { + input = String(input) + + const args = input.split(';') + const mime = args.shift() + const types = mime.split('/') + + if (types.length !== 2 || !types[0] || !types[1]) { + throw new TypeError(`Invalid MIMEType input given: ${mime}`) + } + + const [type, subtype] = types + + this.#type = type.toLowerCase() + // @ts-ignore + this.#params = new MIMEParams(args.map((a) => a.trim().split('=').map((v) => v.trim()))) + this.#subtype = subtype + } + + get type () { + return this.#type + } + + set type (value) { + if (!value || typeof value !== 'string') { + throw new TypeError('MIMEType type must be a string') + } + + this.#type = value + } + + get subtype () { + return this.#subtype + } + + set subtype (value) { + if (!value || typeof value !== 'string') { + throw new TypeError('MIMEType subtype must be a string') + } + + this.#subtype = value + } + + get essence () { + return `${this.type}/${this.subtype}` + } + + get params () { + return this.#params + } + + toString () { + const params = this.params.toString() + + if (params) { + return `${this.essence};${params}` + } + + return this.essence + } + + toJSON () { + return this.toString() + } +} + +export default MIMEType diff --git a/api/npm/module.js b/api/npm/module.js index 021983225e..caef1e1705 100644 --- a/api/npm/module.js +++ b/api/npm/module.js @@ -50,7 +50,9 @@ export async function resolve (specifier, origin = null, options = null) { const pathname = name.pathname.replace(name.value, '.') || '.' try { - pkg.load({ type }) + if (!pkg.load({ type })) { + pkg.load({ prefix, type }) + } const url = pkg.type === type ? pkg.resolve(pathname, { prefix, type }) diff --git a/api/npm/service-worker.js b/api/npm/service-worker.js index 5873bdfeb8..538dd4e2f0 100644 --- a/api/npm/service-worker.js +++ b/api/npm/service-worker.js @@ -23,10 +23,12 @@ export async function onRequest (request, env, ctx) { } const url = new URL(request.url) - const origin = url.origin.replace('npm://', 'socket://') + const origin = (request.headers.get('origin') || url.origin).replace('npm://', 'socket://') const referer = request.headers.get('referer') let specifier = url.pathname.replace('/socket/npm/', '') - const importOrigins = url.searchParams.getAll('origin').concat(url.searchParams.getAll('origin[]')) + const importOrigins = new Set(url.searchParams.getAll('origin').concat(url.searchParams.getAll('origin[]'))) + + importOrigins.add(url.origin.replace('npm://', 'socket://')) if (typeof specifier === 'string') { try { @@ -44,7 +46,7 @@ export async function onRequest (request, env, ctx) { if (URL.canParse(referer, origin)) { const refererURL = new URL(referer, origin) if (refererURL.href.endsWith('/')) { - importOrigins.push(refererURL.href) + importOrigins.add(refererURL.href) } else { importOrigins.push(new URL('./', refererURL).href) } @@ -153,7 +155,7 @@ export async function onRequest (request, env, ctx) { if (resolved.type === 'commonjs') { const proxy = ` import { createRequire } from 'socket:module' - const headers = { 'Runtime-ServiceWorker-Fetch-Mode': 'ignore' } + const headers = {} const require = createRequire('${resolved.origin}', { headers }) const exports = require('${resolved.url}') export default exports?.default ?? exports ?? null diff --git a/api/process.js b/api/process.js index 57d866731f..10436a999c 100644 --- a/api/process.js +++ b/api/process.js @@ -129,7 +129,9 @@ class Process extends EventEmitter { get versions () { return { - socket: this.version + socket: this.version, + uv: primordials.uv.version, + llama: primordials.llama.version } } diff --git a/api/process/signal.js b/api/process/signal.js index 90ae0b40e6..9db6712b4a 100644 --- a/api/process/signal.js +++ b/api/process/signal.js @@ -3,6 +3,7 @@ */ import { signal as constants } from '../os/constants.js' import { SignalEvent } from '../internal/events.js' +import ipc from '../ipc.js' import os from '../os.js' /** @@ -11,7 +12,7 @@ import os from '../os.js' export { constants } -export const channel = new BroadcastChannel('socket.runtime.signal') +export const channel = new ipc.IPCBroadcastChannel('socket.runtime.signal') export const SIGHUP = constants.SIGHUP export const SIGINT = constants.SIGINT diff --git a/api/service-worker/container.js b/api/service-worker/container.js index 3008dd1512..883e800c1b 100644 --- a/api/service-worker/container.js +++ b/api/service-worker/container.js @@ -24,7 +24,7 @@ class ServiceWorkerContainerInternalState { currentWindow = null controller = null sharedWorker = null - channel = new BroadcastChannel('socket.runtime.ServiceWorkerContainer') + channel = new ipc.IPCBroadcastChannel('socket.runtime.ServiceWorkerContainer') ready = new Deferred() init = new Deferred() @@ -320,7 +320,9 @@ export class ServiceWorkerContainer extends EventTarget { async getRegistration (clientURL) { if (globalThis.top && globalThis.window && globalThis.top !== globalThis.window) { - return await globalThis.top.navigator.serviceWorker.getRegistration(clientURL) + try { + return await globalThis.top.navigator.serviceWorker.getRegistration(clientURL) + } catch (err) {} } let scope = clientURL @@ -376,7 +378,9 @@ export class ServiceWorkerContainer extends EventTarget { async getRegistrations () { if (globalThis.top && globalThis.window && globalThis.top !== globalThis.window) { - return await globalThis.top.navigator.serviceWorker.getRegistrations() + try { + return await globalThis.top.navigator.serviceWorker.getRegistrations() + } catch (err) {} } const result = await ipc.request('serviceWorker.getRegistrations') diff --git a/api/service-worker/context.js b/api/service-worker/context.js index 88bf2694ff..5c9c302d39 100644 --- a/api/service-worker/context.js +++ b/api/service-worker/context.js @@ -79,9 +79,11 @@ export class Context { */ async client () { if (this.event.clientId) { + console.log('REQUEST CLIENT', this.event.clientId) return await clients.get(this.event.clientId) } + console.log('NO EVENT CLIENT ID') return null } } diff --git a/api/service-worker/events.js b/api/service-worker/events.js index b750e04e53..a61b33abee 100644 --- a/api/service-worker/events.js +++ b/api/service-worker/events.js @@ -1,4 +1,5 @@ /* global MessagePort */ +import { splitBuffer } from '../util.js' import { Deferred } from '../async.js' import { Context } from './context.js' import application from '../application.js' @@ -321,10 +322,24 @@ export class FetchEvent extends ExtendableEvent { 'auto' ) - const result = await ipc.write( - 'serviceWorker.fetch.response', - params, - new Uint8Array(arrayBuffer) + const buffers = splitBuffer(new Uint8Array(arrayBuffer), 16 * 1024) + for (const buffer of buffers) { + const result = await ipc.write( + 'serviceWorker.fetch.response.write', + params, + buffer + ) + + if (result.err) { + state.reportError(result.err) + handled.resolve() + return + } + } + + const result = await ipc.request( + 'serviceWorker.fetch.response.finish', + params ) if (result.err) { diff --git a/api/service-worker/frame.html b/api/service-worker/frame.html new file mode 100644 index 0000000000..828400c539 --- /dev/null +++ b/api/service-worker/frame.html @@ -0,0 +1,145 @@ + + + + + + + + + + + +

+  
+
diff --git a/api/service-worker/index.html b/api/service-worker/index.html
index 828400c539..be0a7b0d28 100644
--- a/api/service-worker/index.html
+++ b/api/service-worker/index.html
@@ -22,124 +22,23 @@
         value: true
       })
     
-    
-    
+    
     
   
   
-    

   
 
diff --git a/api/service-worker/init.js b/api/service-worker/init.js
index 26287edcf4..c2006a664e 100644
--- a/api/service-worker/init.js
+++ b/api/service-worker/init.js
@@ -5,10 +5,10 @@ import { Notification } from '../notification.js'
 import { sleep } from '../timers.js'
 import globals from '../internal/globals.js'
 import crypto from '../crypto.js'
-import hooks from '../hooks.js'
 import ipc from '../ipc.js'
 
 export const workers = new Map()
+export const channel = new ipc.IPCBroadcastChannel('socket.runtime.serviceWorker')
 
 globals.register('ServiceWorkerContext.workers', workers)
 globals.register('ServiceWorkerContext.info', new Map())
@@ -24,7 +24,7 @@ sharedWorker.port.onmessage = (event) => {
       }
     }
   } else if (event.data?.showNotification && event.data.registration?.id) {
-    onNotificationShow(event, sharedWorker.port)
+    onShowNotification(event, sharedWorker.port)
   } else if (event.data?.getNotifications && event.data.registration?.id) {
     onGetNotifications(event, sharedWorker.port)
   }
@@ -35,13 +35,21 @@ export class ServiceWorkerInstance extends Worker {
   #notifications = []
 
   constructor (filename, options) {
+    options = { ...options }
+    const info = options.info ?? null
+
+    if (info.serializedWorkerArgs) {
+      options.args = JSON.parse(decodeURIComponent(info.serializedWorkerArgs))
+      options.args.index = globalThis.__args.index
+    }
+
     super(filename, {
       name: `ServiceWorker (${options?.info?.pathname ?? filename})`,
       ...options,
       [Symbol.for('socket.runtime.internal.worker.type')]: 'serviceWorker'
     })
 
-    this.#info = options?.info ?? null
+    this.#info = info
     this.addEventListener('message', this.onMessage.bind(this))
   }
 
@@ -98,7 +106,7 @@ export class ServiceWorkerInstance extends Worker {
         log.scrollTop = log.scrollHeight
       }
     } else if (event.data?.showNotification && event.data.registration?.id) {
-      onNotificationShow(event, this)
+      onShowNotification(event, this)
     } else if (event.data?.getNotifications && event.data.registration?.id) {
       onGetNotifications(event, this)
     } else if (event.data?.notificationclose?.id) {
@@ -112,7 +120,9 @@ export class ServiceWorkerInfo {
   url = null
   hash = null
   scope = null
+  scheme = null
   scriptURL = null
+  serializedWorkerArgs = null
 
   constructor (data) {
     for (const key in data) {
@@ -124,7 +134,7 @@ export class ServiceWorkerInfo {
 
     const url = new URL(this.scriptURL)
     this.url = url.toString()
-    this.hash = crypto.murmur3(url.pathname + (this.scope || ''))
+    this.hash = crypto.murmur3(this.scheme + url.pathname + (this.scope || ''))
   }
 
   get pathname () {
@@ -195,7 +205,7 @@ export async function onFetch (event) {
       }
     })(),
     new Promise((resolve) => {
-      globalThis.top.addEventListener(
+      globalThis.addEventListener(
         'serviceWorker.activate',
         async function (event) {
           // @ts-ignore
@@ -240,7 +250,7 @@ export async function onFetch (event) {
   worker.postMessage({ fetch: { ...info, client, request } })
 }
 
-export function onNotificationShow (event, target) {
+export function onShowNotification (event, target) {
   for (const worker of workers.values()) {
     if (worker.info.id === event.data.registration.id) {
       let notification = null
@@ -343,22 +353,14 @@ export function onGetNotifications (event, target) {
 
 export default null
 
-globalThis.top.addEventListener('serviceWorker.register', onRegister)
-globalThis.top.addEventListener('serviceWorker.unregister', onUnregister)
-globalThis.top.addEventListener('serviceWorker.skipWaiting', onSkipWaiting)
-globalThis.top.addEventListener('serviceWorker.activate', onActivate)
-globalThis.top.addEventListener('serviceWorker.fetch', onFetch)
+globalThis.addEventListener('serviceWorker.register', onRegister)
+globalThis.addEventListener('serviceWorker.unregister', onUnregister)
+globalThis.addEventListener('serviceWorker.skipWaiting', onSkipWaiting)
+globalThis.addEventListener('serviceWorker.activate', onActivate)
+globalThis.addEventListener('serviceWorker.fetch', onFetch)
 
-hooks.onReady(async () => {
-  // notify top frame that the service worker init module is ready
-  globalThis.top.postMessage({
-    __service_worker_frame_init: true
-  })
-
-  const result = await ipc.request('serviceWorker.getRegistrations')
-  if (Array.isArray(result.data)) {
-    for (const info of result.data) {
-      await navigator.serviceWorker.register(info.scriptURL, info)
-    }
+channel.addEventListener('message', (e) => {
+  if (e.data?.type?.startsWith('serviceWorker.')) {
+    globalThis.dispatchEvent(new CustomEvent(e.data.type, e.data))
   }
 })
diff --git a/api/service-worker/instance.js b/api/service-worker/instance.js
index caa1c5477d..8e801d77ef 100644
--- a/api/service-worker/instance.js
+++ b/api/service-worker/instance.js
@@ -32,7 +32,7 @@ export function createServiceWorker (
     }
   }
 
-  const channel = new BroadcastChannel('socket.runtime.serviceWorker.state')
+  const channel = new ipc.IPCBroadcastChannel('socket.runtime.serviceWorker.state')
 
   // events
   const eventTarget = new EventTarget()
diff --git a/api/service-worker/main.js b/api/service-worker/main.js
new file mode 100644
index 0000000000..30f881ee82
--- /dev/null
+++ b/api/service-worker/main.js
@@ -0,0 +1,161 @@
+import hooks from '../hooks.js'
+import ipc from '../ipc.js'
+
+const frames = new Map()
+const channels = new Map()
+const defaultHost = globalThis.location.origin
+
+globalThis.top.addEventListener('serviceWorker.register', onRegister)
+globalThis.top.addEventListener('serviceWorker.unregister', onUnregister)
+globalThis.top.addEventListener('serviceWorker.skipWaiting', onSkipWaiting)
+globalThis.top.addEventListener('serviceWorker.activate', onActivate)
+globalThis.top.addEventListener('serviceWorker.fetch', onFetch)
+
+hooks.onReady(async () => {
+  // notify top frame that the service worker init module is ready
+  globalThis.top.postMessage({
+    __service_worker_frame_init: true
+  }, '*')
+
+  const result = await ipc.request('serviceWorker.getRegistrations')
+  if (Array.isArray(result.data)) {
+    for (const info of result.data) {
+      await navigator.serviceWorker.register(info.scriptURL, info)
+    }
+  }
+})
+
+async function onRegister (event) {
+  const url = new URL(event.detail.scriptURL)
+  const origin = url.origin || defaultHost
+  const frame = (
+    frames.get(origin) ||
+    globalThis.document.createElement('iframe')
+  )
+
+  const channel = (
+    channels.get(origin) ||
+    new ipc.IPCBroadcastChannel('socket.runtime.serviceWorker', {
+      origin
+    })
+  )
+
+  if (!frames.has(origin)) {
+    frame.src = new URL('/socket/service-worker/frame.html', origin)
+    frame.setAttribute('sandbox', [
+      'allow-storage-access-by-user-activation',
+      'allow-same-origin',
+      'allow-scripts'
+    ].join(' '))
+
+    frame.ready = new Promise((resolve) => {
+      frame.addEventListener('load', () => setTimeout(resolve, 500))
+    })
+
+    globalThis.document.body.appendChild(frame)
+
+    channels.set(origin, channel)
+    frames.set(origin, frame)
+  }
+
+  await frame.ready
+
+  channel.postMessage({
+    type: 'serviceWorker.register',
+    detail: event.detail
+  })
+}
+
+async function onUnregister (event) {
+  const url = new URL(event.detail.scriptURL)
+  const origin = url.origin || defaultHost
+  const frame = frames.get(origin)
+  const channel = channels.get(origin)
+
+  if (!frame || !channel) {
+    return
+  }
+
+  await frame.ready
+
+  channel.postMessage({
+    type: 'serviceWorker.unregister',
+    detail: event.detail
+  })
+}
+
+async function onSkipWaiting (event) {
+  const url = new URL(event.detail.scriptURL)
+  const origin = url.origin || defaultHost
+  const frame = frames.get(origin)
+  const channel = channels.get(origin)
+
+  if (!frame || !channel) {
+    return
+  }
+
+  await frame.ready
+  channel.postMessage({
+    type: 'serviceWorker.skipWaiting',
+    detail: event.detail
+  })
+}
+
+async function onActivate (event) {
+  const url = new URL(event.detail.scriptURL)
+  const origin = url.origin || defaultHost
+  const frame = frames.get(origin)
+  const channel = channels.get(origin)
+
+  if (!frame || !channel) {
+    return
+  }
+
+  await frame.ready
+
+  channel.postMessage({
+    type: 'serviceWorker.activate',
+    detail: event.detail
+  })
+}
+
+async function onFetch (event) {
+  const url = new URL(event.detail.scriptURL)
+  const origin = url.origin || defaultHost
+  const frame = (
+    frames.get(origin) ||
+    globalThis.document.createElement('iframe')
+  )
+
+  const channel = (
+    channels.get(origin) ||
+    new ipc.IPCBroadcastChannel('socket.runtime.serviceWorker', {
+      origin
+    })
+  )
+
+  if (!frames.has(origin)) {
+    frames.set(origin, frame)
+    channels.set(origin, channel)
+
+    frame.src = new URL('/socket/service-worker/frame.html', origin)
+    frame.setAttribute('sandbox', [
+      'allow-storage-access-by-user-activation',
+      'allow-same-origin',
+      'allow-scripts'
+    ].join(' '))
+
+    globalThis.document.body.appendChild(frame)
+
+    frame.ready = new Promise((resolve) => {
+      frame.addEventListener('load', () => setTimeout(resolve, 500))
+    })
+  }
+
+  await frame.ready
+
+  channel.postMessage({
+    type: 'serviceWorker.fetch',
+    detail: event.detail
+  })
+}
diff --git a/api/service-worker/state.js b/api/service-worker/state.js
index d9f8692cc6..aac9ed26aa 100644
--- a/api/service-worker/state.js
+++ b/api/service-worker/state.js
@@ -2,7 +2,7 @@ import application from '../application.js'
 import debug from './debug.js'
 import ipc from '../ipc.js'
 
-export const channel = new BroadcastChannel('socket.runtime.serviceWorker.state')
+export const channel = new ipc.IPCBroadcastChannel('socket.runtime.serviceWorker.state')
 
 const descriptors = {
   channel: {
@@ -169,6 +169,12 @@ channel.addEventListener('message', (event) => {
 })
 
 if (globalThis.document) {
+  const timers = {
+    blur: 0,
+    focus: 0,
+    visibilitychange: 0
+  }
+
   channel.addEventListener('message', async (event) => {
     if (event.data?.client?.id === globalThis.__args.client.id) {
       if (event.data.client.focus === true) {
@@ -182,30 +188,39 @@ if (globalThis.document) {
   })
 
   globalThis.document.addEventListener('visibilitychange', (event) => {
-    channel.postMessage({
-      client: {
-        id: globalThis.__args.client.id,
-        visibilityState: globalThis.document.visibilityState
-      }
-    })
+    clearTimeout(timers.visibilityState)
+    timers.visibilityState = setTimeout(() => {
+      channel.postMessage({
+        client: {
+          id: globalThis.__args.client.id,
+          visibilityState: globalThis.document.visibilityState
+        }
+      })
+    }, 250)
   })
 
   globalThis.addEventListener('focus', (event) => {
-    channel.postMessage({
-      client: {
-        id: globalThis.__args.client.id,
-        focused: true
-      }
-    })
+    clearTimeout(timers.focus)
+    timers.focus = setTimeout(() => {
+      channel.postMessage({
+        client: {
+          id: globalThis.__args.client.id,
+          focused: true
+        }
+      })
+    }, 250)
   })
 
   globalThis.addEventListener('blur', (event) => {
-    channel.postMessage({
-      client: {
-        id: globalThis.__args.client.id,
-        focused: false
-      }
-    })
+    clearTimeout(timers.blur)
+    timers.blur = setTimeout(() => {
+      channel.postMessage({
+        client: {
+          id: globalThis.__args.client.id,
+          focused: false
+        }
+      })
+    }, 250)
   })
 }
 
diff --git a/api/shared-worker/frame.html b/api/shared-worker/frame.html
new file mode 100644
index 0000000000..59720ca7d4
--- /dev/null
+++ b/api/shared-worker/frame.html
@@ -0,0 +1,145 @@
+
+
+  
+    
+    
+    
+    
+    
+    
+  
+  
+    

+  
+
diff --git a/api/shared-worker/index.html b/api/shared-worker/index.html
index dba4d4fc0f..5251fd6ce7 100644
--- a/api/shared-worker/index.html
+++ b/api/shared-worker/index.html
@@ -22,124 +22,23 @@
         value: true
       })
     
-    
-    
+    
     
   
   
-    

   
 
diff --git a/api/shared-worker/index.js b/api/shared-worker/index.js
index 95b19b96d3..bb85af1bd4 100644
--- a/api/shared-worker/index.js
+++ b/api/shared-worker/index.js
@@ -1,5 +1,6 @@
 /* global XMLHttpRequest, ErrorEvent */
 import application from '../application.js'
+import serialize from '../internal/serialize.js'
 import location from '../location.js'
 import process from '../process.js'
 import crypto from '../crypto.js'
@@ -12,7 +13,7 @@ export const SHARED_WORKER_WINDOW_INDEX = 46
 export const SHARED_WORKER_WINDOW_TITLE = 'socket:shared-worker'
 export const SHARED_WORKER_WINDOW_PATH = '/socket/shared-worker/index.html'
 
-export const channel = new BroadcastChannel('socket.runtime.sharedWorker')
+export const channel = new ipc.IPCBroadcastChannel('socket.runtime.sharedWorker')
 export const workers = new Map()
 
 channel.addEventListener('message', (event) => {
@@ -32,14 +33,19 @@ channel.addEventListener('message', (event) => {
 })
 
 export async function init (sharedWorker, options) {
-  await getContextWindow()
-  channel.postMessage({
-    connect: {
-      scriptURL: options.scriptURL,
-      client: client.toJSON(),
-      name: options.name,
-      port: sharedWorker.port,
-      id: sharedWorker.id
+  const currentWindow = await application.getCurrentWindow()
+  const window = await getContextWindow()
+  await currentWindow.send({
+    event: 'connect',
+    window: window.index,
+    value: {
+      connect: {
+        scriptURL: options.scriptURL,
+        client: client.toJSON(),
+        name: options.name,
+        port: serialize(ipc.findIPCMessageTransfers(new Set(), sharedWorker.port)),
+        id: sharedWorker.id
+      }
     }
   })
 
@@ -74,7 +80,12 @@ export class SharedWorker extends EventTarget {
       const request = new XMLHttpRequest()
       request.open('GET', String(aURL), false)
       request.send()
-      const blob = new Blob([request.responseText || request.response], { type: 'application/javascript' })
+
+      const blob = new Blob(
+        [request.responseText || request.response],
+        { type: 'application/javascript' }
+      )
+
       aURL = URL.createObjectURL(blob)
     }
 
@@ -151,6 +162,8 @@ export async function getContextWindow () {
       index: SHARED_WORKER_WINDOW_INDEX,
       title: SHARED_WORKER_WINDOW_TITLE,
       path: SHARED_WORKER_WINDOW_PATH,
+      width: '80%',
+      height: '80%',
       config: {
         webview_watch_reload: false
       }
@@ -183,8 +196,9 @@ export async function getContextWindow () {
   await ready
   contextWindow = await pendingContextWindow
   contextWindow.ready = ready
-
-  await contextWindow.hide()
+  if (!process.env.SOCKET_RUNTIME_SHARED_WORKER_DEBUG) {
+    await contextWindow.hide()
+  }
 
   return contextWindow
 }
diff --git a/api/shared-worker/init.js b/api/shared-worker/init.js
index 847b27cd03..301c46fe52 100644
--- a/api/shared-worker/init.js
+++ b/api/shared-worker/init.js
@@ -1,7 +1,9 @@
 /* global Worker */
 import { channel } from './index.js'
+import serialize from '../internal/serialize.js'
 import globals from '../internal/globals.js'
 import crypto from '../crypto.js'
+import ipc from '../ipc.js'
 
 export const workers = new Map()
 export { channel }
@@ -91,6 +93,10 @@ export class SharedWorkerInfo {
     const url = new URL(this.scriptURL)
     this.url = url.toString()
     this.hash = crypto.murmur3(url.toString())
+
+    if (this.port) {
+      this.port = serialize(ipc.findIPCMessageTransfers(new Set(), this.port))
+    }
   }
 
   get pathname () {
@@ -112,6 +118,20 @@ export async function onInstall (event) {
   workers.set(info.hash, worker)
   globals.get('SharedWorkerContext.info').set(info.hash, info)
   worker.postMessage({ install: info })
+
+  try {
+    await new Promise((resolve, reject) => {
+      channel.addEventListener('message', (event) => {
+        if (event.data?.error?.id === info.id) {
+          reject(new Error(event.data.error.message))
+        } else if (event.data?.installed?.id === info.id) {
+          resolve(undefined)
+        }
+      })
+    })
+  } catch (err) {
+    console.error(err)
+  }
 }
 
 export async function onUninstall (event) {
@@ -135,25 +155,11 @@ export async function onConnect (event) {
   }
 
   if (!workers.has(info.hash)) {
-    onInstall(new MessageEvent('message', {
+    await onInstall(new MessageEvent('message', {
       data: {
         install: event.data.connect
       }
     }))
-
-    try {
-      await new Promise((resolve, reject) => {
-        channel.addEventListener('message', (event) => {
-          if (event.data?.error?.id === info.id) {
-            reject(new Error(event.data.error.message))
-          } else if (event.data?.installed?.id === info.id) {
-            resolve()
-          }
-        })
-      })
-    } catch (err) {
-      console.error(err)
-    }
   }
 
   const worker = workers.get(info.hash)
diff --git a/api/shared-worker/main.js b/api/shared-worker/main.js
new file mode 100644
index 0000000000..bcbd159914
--- /dev/null
+++ b/api/shared-worker/main.js
@@ -0,0 +1,46 @@
+import ipc from '../ipc.js'
+
+const frames = new Map()
+const channels = new Map()
+const defaultHost = globalThis.location.origin
+
+globalThis.addEventListener('connect', async (event) => {
+  if (event.detail?.connect) {
+    const url = new URL(event.detail.connect.scriptURL)
+    const origin = url.origin || defaultHost
+    const frame = (
+      frames.get(origin) ||
+      globalThis.document.createElement('iframe')
+    )
+
+    const channel = (
+      channels.get(origin) ||
+      new ipc.IPCBroadcastChannel('socket.runtime.sharedWorker', {
+        origin
+      })
+    )
+
+    if (!frames.has(origin)) {
+      frame.src = new URL('/socket/shared-worker/frame.html', origin)
+      frame.setAttribute('sandbox', [
+        'allow-storage-access-by-user-activation',
+        'allow-same-origin',
+        'allow-scripts'
+      ].join(' '))
+
+      frame.ready = new Promise((resolve) => {
+        frame.addEventListener('load', () => setTimeout(resolve, 100))
+        setTimeout(resolve, 500)
+      })
+
+      globalThis.document.body.appendChild(frame)
+
+      channels.set(origin, channel)
+      frames.set(origin, frame)
+    }
+
+    await frame.ready
+
+    channel.postMessage(event.detail)
+  }
+})
diff --git a/api/shared-worker/worker.js b/api/shared-worker/worker.js
index ffd4f78d1d..57d40b0720 100644
--- a/api/shared-worker/worker.js
+++ b/api/shared-worker/worker.js
@@ -186,12 +186,13 @@ export async function onMessage (event) {
       })
     }
 
-    channel.postMessage({ installed: { id: state.id } })
     debug(
       '[%s]: SharedWorker (%s) installed',
       new URL(scriptURL).pathname.replace(/^\/socket\//, 'socket:'),
       state.id
     )
+
+    channel.postMessage({ installed: { id: state.id } })
     return
   }
 
@@ -217,8 +218,6 @@ export async function onMessage (event) {
       value: Object.seal(Object.freeze([connection.port]))
     })
 
-    globalThis.dispatchEvent(connectEvent)
-
     debug(
       '[%s]: SharedWorker (%s) connection from client (%s/%s) at %s',
       new URL(state.sharedWorker.scriptURL).pathname.replace(/^\/socket\//, 'socket:'),
@@ -230,6 +229,7 @@ export async function onMessage (event) {
       ].filter(Boolean).join('-'),
       connection.client.location
     )
+    globalThis.dispatchEvent(connectEvent)
     // eslint-disable-next-line
     return
   }
diff --git a/api/util.js b/api/util.js
index ea13e49b80..760627ab79 100644
--- a/api/util.js
+++ b/api/util.js
@@ -1,8 +1,9 @@
 import { IllegalConstructorError } from './errors.js'
+import { MIMEParams } from './mime/params.js'
+import { MIMEType } from './mime/type.js'
 import { Buffer } from './buffer.js'
 import { URL } from './url.js'
 import types from './util/types.js'
-import mime from './mime.js'
 
 import * as exports from './util.js'
 
@@ -1008,7 +1009,5 @@ export function deprecate (...args) {
   // noop
 }
 
-export const MIMEType = mime.MIMEType
-export const MIMEParams = mime.MIMEParams
-
+export { MIMEType, MIMEParams }
 export default exports
diff --git a/api/vm.js b/api/vm.js
index 55b09de77d..c65f3d61e9 100644
--- a/api/vm.js
+++ b/api/vm.js
@@ -26,7 +26,7 @@
 
 /* eslint-disable no-new-func */
 /* global ErrorEvent, EventTarget, MessagePort */
-import { maybeMakeError } from './ipc.js'
+import ipc, { maybeMakeError } from './ipc.js'
 import { SharedWorker } from './shared-worker/index.js'
 import { isESMSource } from './util.js'
 import application from './application.js'
@@ -110,7 +110,7 @@ function convertSourceToString (source) {
  * Shared broadcast for virtual machaines
  * @type {BroadcastChannel}
  */
-export const channel = new BroadcastChannel('socket.runtime.vm')
+export const channel = new ipc.IPCBroadcastChannel('socket.runtime.vm')
 
 /**
  * @ignore
diff --git a/api/window.js b/api/window.js
index b8cf3b6aec..10ff71c29e 100644
--- a/api/window.js
+++ b/api/window.js
@@ -35,7 +35,7 @@ export function formatURL (url) {
 export class ApplicationWindow {
   #id = null
   #index
-  #options
+  #state
   #channel = null
   #senderWindowIndex = globalThis.__args.index
   #listeners = {}
@@ -44,21 +44,21 @@ export class ApplicationWindow {
   static constants = statuses
   static hotkey = hotkey
 
-  constructor ({ index, ...options }) {
-    this.#id = options?.id
+  constructor ({ index, ...state }) {
+    this.#id = state?.id
     this.#index = index
-    this.#options = options
+    this.#state = state
     this.#channel = new BroadcastChannel(`socket.runtime.window.${this.#index}`)
   }
 
-  #updateOptions (response) {
+  #updateState (response) {
     const { data, err } = response
     if (err) {
       throw new Error(err)
     }
-    const { id, index, ...options } = data
+    const { id, index, ...state } = data
     this.#id = id ?? null
-    this.#options = options
+    this.#state = state
     return data
   }
 
@@ -85,6 +85,10 @@ export class ApplicationWindow {
     return hotkey
   }
 
+  get state () {
+    return this.#state
+  }
+
   /**
    * The broadcast channel for this window.
    * @type {BroadcastChannel}
@@ -93,14 +97,56 @@ export class ApplicationWindow {
     return this.#channel
   }
 
+  /**
+   * Get the size of the window
+   * @return {{ width: number, height: number }} - the size of the window
+   */
+  get size () {
+    return {
+      width: this.#state.width,
+      height: this.#state.height
+    }
+  }
+
+  get location () {
+    return this.#state.location ?? null
+  }
+
+  /**
+   * get  the position of the window
+   * @return {{ x: number, y: number }} - the position of the window
+   */
+  get position () {
+    return {
+      x: this.#state.x,
+      y: this.#state.y
+    }
+  }
+
+  /**
+   * get  the title of the window
+   * @return {string} - the title of the window
+   */
+  get title () {
+    return this.#state.title
+  }
+
+  /**
+   * get  the status of the window
+   * @return {string} - the status of the window
+   */
+  get status () {
+    return this.#state.status
+  }
+
   /**
    * Get the size of the window
    * @return {{ width: number, height: number }} - the size of the window
    */
   getSize () {
     return {
-      width: this.#options.width,
-      height: this.#options.height
+      width: this.#state.width,
+      height: this.#state.height
     }
   }
 
@@ -110,8 +156,8 @@ export class ApplicationWindow {
    */
   getPosition () {
     return {
-      x: this.#options.x,
-      y: this.#options.y
+      x: this.#state.x,
+      y: this.#state.y
     }
   }
 
@@ -120,7 +166,7 @@ export class ApplicationWindow {
    * @return {string} - the title of the window
    */
   getTitle () {
-    return this.#options.title
+    return this.#state.title
   }
 
   /**
@@ -128,7 +174,7 @@ export class ApplicationWindow {
    * @return {string} - the status of the window
    */
   getStatus () {
-    return this.#options.status
+    return this.#state.status
   }
 
   /**
@@ -152,7 +198,7 @@ export class ApplicationWindow {
    */
   async show () {
     const response = await ipc.request('window.show', { index: this.#senderWindowIndex, targetWindowIndex: this.#index })
-    return this.#updateOptions(response)
+    return this.#updateState(response)
   }
 
   /**
@@ -161,7 +207,7 @@ export class ApplicationWindow {
    */
   async hide () {
     const response = await ipc.request('window.hide', { index: this.#senderWindowIndex, targetWindowIndex: this.#index })
-    return this.#updateOptions(response)
+    return this.#updateState(response)
   }
 
   /**
@@ -170,7 +216,7 @@ export class ApplicationWindow {
    */
   async maximize () {
     const response = await ipc.request('window.maximize', { index: this.#senderWindowIndex, targetWindowIndex: this.#index })
-    return this.#updateOptions(response)
+    return this.#updateState(response)
   }
 
   /**
@@ -179,7 +225,7 @@ export class ApplicationWindow {
    */
   async minimize () {
     const response = await ipc.request('window.minimize', { index: this.#senderWindowIndex, targetWindowIndex: this.#index })
-    return this.#updateOptions(response)
+    return this.#updateState(response)
   }
 
   /**
@@ -188,7 +234,7 @@ export class ApplicationWindow {
    */
   async restore () {
     const response = await ipc.request('window.restore', { index: this.#senderWindowIndex, targetWindowIndex: this.#index })
-    return this.#updateOptions(response)
+    return this.#updateState(response)
   }
 
   /**
@@ -198,7 +244,7 @@ export class ApplicationWindow {
    */
   async setTitle (title) {
     const response = await ipc.request('window.setTitle', { index: this.#senderWindowIndex, targetWindowIndex: this.#index, value: title })
-    return this.#updateOptions(response)
+    return this.#updateState(response)
   }
 
   /**
@@ -241,7 +287,7 @@ export class ApplicationWindow {
     }
 
     const response = await ipc.request('window.setSize', options)
-    return this.#updateOptions(response)
+    return this.#updateState(response)
   }
 
   /**
@@ -288,7 +334,7 @@ export class ApplicationWindow {
     }
 
     const response = await ipc.request('window.setPosition', options)
-    return this.#updateOptions(response)
+    return this.#updateState(response)
   }
 
   /**
@@ -298,7 +344,7 @@ export class ApplicationWindow {
    */
   async navigate (path) {
     const response = await ipc.request('window.navigate', { index: this.#senderWindowIndex, targetWindowIndex: this.#index, url: formatURL(path) })
-    return this.#updateOptions(response)
+    return this.#updateState(response)
   }
 
   /**
@@ -324,7 +370,7 @@ export class ApplicationWindow {
    */
   async setBackgroundColor (opts) {
     const response = await ipc.request('window.setBackgroundColor', { index: this.#senderWindowIndex, targetWindowIndex: this.#index, ...opts })
-    return this.#updateOptions(response)
+    return this.#updateState(response)
   }
 
   /**
@@ -493,6 +539,16 @@ export class ApplicationWindow {
     }
   }
 
+  /**
+   * Updates wnidow state
+   * @param {string} title - the title of the window
+   * @return {Promise}
+   */
+  async update () {
+    const response = await ipc.request('window', { index: this.#index })
+    return this.#updateState(response)
+  }
+
   // public EventEmitter methods
   /**
    * Adds a listener to the window.
diff --git a/bin/build-runtime-library.sh b/bin/build-runtime-library.sh
index 30971e4900..7d718d20aa 100755
--- a/bin/build-runtime-library.sh
+++ b/bin/build-runtime-library.sh
@@ -95,22 +95,23 @@ done
 declare objects=()
 declare sources=(
   $(find "$root"/src/app/*.cc)
-  $(find "$root"/src/core/*.cc)
-  $(find "$root"/src/core/modules/*.cc)
-  $(find "$root"/src/core/json/*.cc)
+  $(find "$root"/src/runtime/*.cc)
+  $(find "$root"/src/runtime/debug/*.cc)
+  $(find "$root"/src/runtime/http/*.cc)
+  $(find "$root"/src/runtime/ipc/*.cc)
+  $(find "$root"/src/runtime/json/*.cc)
+  $(find "$root"/src/runtime/modules/*.cc)
+  $(find "$root"/src/runtime/platform/*.cc)
+  $(find "$root"/src/runtime/serviceworker/*.cc)
   $(find "$root"/src/extension/*.cc)
-  $(find "$root"/src/ipc/*.cc)
-  $(find "$root"/src/platform/*.cc)
-  $(find "$root"/src/serviceworker/*.cc)
-  #$(find "$root"/src/sharedworker/*.cc)
+  "$root/src/runtime/window/manager.cc"
+  "$root/src/runtime/window/dialog.cc"
+  "$root/src/runtime/window/hotkey.cc"
   "$root/build/llama/common/common.cpp"
   "$root/build/llama/common/sampling.cpp"
   "$root/build/llama/common/json-schema-to-grammar.cpp"
   "$root/build/llama/common/grammar-parser.cpp"
   "$root/build/llama/llama.cpp"
-  "$root/src/window/manager.cc"
-  "$root/src/window/dialog.cc"
-  "$root/src/window/hotkey.cc"
 )
 
 declare cflags
@@ -126,7 +127,7 @@ if [[ "$platform" = "android" ]]; then
 
   clang="$(android_clang "$ANDROID_HOME" "$NDK_VERSION" "$host" "$host_arch" "++")"
   clang_target="$(android_clang_target "$arch")"
-  sources+=("$root/src/core/process/unix.cc")
+  sources+=("$root/src/runtime/process/unix.cc")
   sources+=($(find "$root/src/platform/android"/*.cc))
   sources+=("$root/src/window/android.cc")
 elif [[ "$host" = "Darwin" ]]; then
@@ -137,14 +138,14 @@ elif [[ "$host" = "Darwin" ]]; then
   elif (( TARGET_IPHONE_SIMULATOR )); then
     clang="xcrun -sdk iphonesimulator "$clang""
   else
-    sources+=("$root/src/core/process/unix.cc")
+    sources+=("$root/src/runtime/process/unix.cc")
   fi
 elif [[ "$host" = "Linux" ]]; then
   sources+=("$root/src/window/linux.cc")
-  sources+=("$root/src/core/process/unix.cc")
+  sources+=("$root/src/runtime/process/unix.cc")
 elif [[ "$host" = "Win32" ]]; then
   sources+=("$root/src/window/win.cc")
-  sources+=("$root/src/core/process/win.cc")
+  sources+=("$root/src/runtime/process/win.cc")
 fi
 
 cflags+=($(ARCH="$arch" "$root/bin/cflags.sh"))
diff --git a/bin/clean.sh b/bin/clean.sh
index f27b0f62da..2a9550013a 100755
--- a/bin/clean.sh
+++ b/bin/clean.sh
@@ -76,7 +76,6 @@ elif [ -n "$arch" ] || [ -n "$platform" ]; then
     "$root/build/"$arch-$platform/cli            \
     "$root/build/"$arch-$platform/runtime        \
     "$root/build/"$arch-$platform/ipc            \
-    "$root/build/"$arch-$platform/core           \
     "$root/build/"$arch-$platform/objects        \
     "$root/build/"$arch-$platform/process        \
     "$root/build/"$arch-$platform/window         \
@@ -95,7 +94,6 @@ else
     "$root"/build/*/bin            \
     "$root"/build/*/cli            \
     "$root"/build/*/runtime        \
-    "$root"/build/*/core           \
     "$root"/build/*/ipc            \
     "$root"/build/*/objects        \
     "$root"/build/*/process        \
diff --git a/bin/install.sh b/bin/install.sh
index c2d72f4779..d22194c07c 100755
--- a/bin/install.sh
+++ b/bin/install.sh
@@ -1067,7 +1067,7 @@ function _check_compiler_features {
   cflags+=("-I$root")
 
   $CXX "${cflags[@]}" "${ldflags[@]}" - -o /dev/null >/dev/null << EOF_CC
-    #include "src/core/core.hh"
+    #include "src/runtime/runtime.hh"
     int main () { return 0; }
 EOF_CC
 
diff --git a/src/app/app.hh b/src/app/app.hh
index 52df6a8ce8..c8f09fb474 100644
--- a/src/app/app.hh
+++ b/src/app/app.hh
@@ -1,6 +1,8 @@
 #ifndef SOCKET_RUNTIME_APP_APP_H
 #define SOCKET_RUNTIME_APP_APP_H
 
+#include "../runtime.hh"
+
 #include "../core/core.hh"
 #include "../serviceworker/container.hh"
 #include "../window/window.hh"
@@ -92,7 +94,7 @@ namespace SSC {
       WindowManager windowManager;
       ServiceWorkerContainer serviceWorkerContainer;
       SharedPointer core = nullptr;
-      Map userConfig;
+      Map userConfig;
 
     #if SOCKET_RUNTIME_PLATFORM_ANDROID
       /**
@@ -104,7 +106,7 @@ namespace SSC {
       App (
         JNIEnv* env,
         jobject self,
-        SharedPointer core = SharedPointer(new Core())
+        SharedPointer core = std::make_shared()
       );
     #else
       /**
@@ -118,7 +120,7 @@ namespace SSC {
       #else
         int instanceId = 0,
       #endif
-        SharedPointer core = SharedPointer(new Core())
+        SharedPointer core = std::make_shared()
       );
     #endif
 
diff --git a/src/cli/cli.hh b/src/cli/cli.hh
index c298c74fad..aff18b9d36 100644
--- a/src/cli/cli.hh
+++ b/src/cli/cli.hh
@@ -1,19 +1,15 @@
 #ifndef SOCKET_RUNTIME_CLI_H
 #define SOCKET_RUNTIME_CLI_H
 
-#include "../platform/platform.hh"
-#include "../core/env.hh"
+#include "../runtime/platform.hh"
+#include "../runtime/env.hh"
 
 #include 
 
-#ifndef SOCKET_CLI
-#define SOCKET_CLI 1
-#endif
-
-namespace SSC::CLI {
+namespace ssc::cli {
   inline void notify (int signal) {
   #if !defined(_WIN32)
-    static auto ppid = Env::get("SSC_CLI_PID");
+    static auto ppid = runtime::env::get("SSC_CLI_PID");
     static auto pid = ppid.size() > 0 ? std::stoi(ppid) : 0;
     if (pid > 0) {
       kill(pid, signal);
diff --git a/src/cli/cli.cc b/src/cli/main.cc
similarity index 99%
rename from src/cli/cli.cc
rename to src/cli/main.cc
index cad9053e2e..1db2d61bfd 100644
--- a/src/cli/cli.cc
+++ b/src/cli/main.cc
@@ -73,15 +73,15 @@ Thread* sourcesWatcherSupportThread = nullptr;
 
 Path targetPath;
 String settingsSource = "";
-Map settings;
-Map rc;
+Map<> settings;
+Map<> rc;
 
 auto start = system_clock::now();
 
 bool flagDebugMode = true;
 bool flagVerboseMode = false;
 bool flagQuietMode = false;
-Map defaultTemplateAttrs;
+Map<> defaultTemplateAttrs;
 
 #if SOCKET_RUNTIME_PLATFORM_APPLE
 Atomic checkLogStore = true;
@@ -106,7 +106,8 @@ void log (const String s) {
   start = std::chrono::system_clock::now();
 }
 
-inline Map& extendMap (Map& dst, const Map& src) {
+template 
+inline Map& extendMap (Map& dst, const Map& src) {
   for (const auto& tuple : src) {
     dst[tuple.first] = tuple.second;
   }
@@ -409,7 +410,7 @@ unsigned short createLogSocket() {
 #endif
 
 void handleBuildPhaseForUser (
-  const Map settings,
+  const Map<> settings,
   const String& targetPlatform,
   const Path pathResourcesRelativeToUserBuild,
   const Path& cwd,
@@ -506,7 +507,7 @@ void handleBuildPhaseForUser (
 }
 
 Vector handleBuildPhaseForCopyMappedFiles (
-  const Map settings,
+  const Map<> settings,
   const String& targetPlatform,
   const Path pathResourcesRelativeToUserBuild,
   bool includeBuildCopyFiles = true
@@ -1026,7 +1027,7 @@ int runApp (const Path& path, const String& args) {
   return runApp(path, args, false);
 }
 
-void runIOSSimulator (const Path& path, Map& settings) {
+void runIOSSimulator (const Path& path, Map<>& settings) {
   #ifndef _WIN32
   String uuid;
   bool booted = false;
@@ -1626,7 +1627,7 @@ void initializeRC (Path targetPath) {
   }
 
   if (fs::exists(path) && fs::is_regular_file(path)) {
-    extendMap(rc, INI::parse(tmpl(readFile(path), Map {
+    extendMap(rc, INI::parse(tmpl(readFile(path), Map<> {
       {"platform.arch", platform.arch},
       {"platform.arch.short", replace(platform.arch, "x86_64", "x64")},
       {"platform.os", platform.os},
@@ -1700,7 +1701,7 @@ bool isSetupComplete (SSC::String platform) {
   return funcs[platform]();
 }
 
-void run (const String& targetPlatform, Map& settings, const Paths& paths, const bool& flagDebugMode, const bool& flagRunHeadless, const String& argvForward, AndroidCliState& androidState) {
+void run (const String& targetPlatform, Map<>& settings, const Paths& paths, const bool& flagDebugMode, const bool& flagRunHeadless, const String& argvForward, AndroidCliState& androidState) {
   if (targetPlatform == "ios-simulator") {
     String app = (settings["build_name"] + ".app");
     auto pathToApp = paths.platformSpecificOutputPath / app;
@@ -1742,7 +1743,7 @@ struct CommandLineOption {
 using CommandLineOptions = Vector;
 
 struct optionsAndEnv {
-  Map optionsWithValue;
+  Map<> optionsWithValue;
   std::unordered_set optionsWithoutValue;
   Vector envs;
 };
@@ -1753,7 +1754,7 @@ optionsAndEnv parseCommandLineOptions (
   const String& subcommand
 ) {
   optionsAndEnv result;
-  Map optionsWithValue;
+  Map<> optionsWithValue;
   std::unordered_set optionsWithoutValue;
   std::vector envs;
 
@@ -2036,7 +2037,7 @@ int main (int argc, char* argv[]) {
     const String& subcommand,
     const CommandLineOptions& availableOptions,
     const bool& needsConfig,
-    std::function)> subcommandHandler
+    Function, UnorderedSet)> subcommandHandler
   ) -> void {
     if (argv[1] == subcommand) {
       auto commandlineOptions = std::span(const_cast(argv), argc).subspan(2, numberOfOptions);
@@ -2133,7 +2134,7 @@ int main (int argc, char* argv[]) {
           );
         }
 
-        settingsSource = tmpl(ini, Map {
+        settingsSource = tmpl(ini, Map<> {
           {"platform.arch", platform.arch},
           {"platform.arch.short", replace(platform.arch, "x86_64", "x64")},
           {"platform.os", platform.os},
@@ -2166,7 +2167,7 @@ int main (int argc, char* argv[]) {
         // project's settings in `socket.ini`:
         // [settings.ios]
         // simulator_device = "My local device"
-        Map tmp;
+        Map<> tmp;
         for (const auto& tuple : rc) {
           if (tuple.first.starts_with("settings_")) {
             auto key = replace(tuple.first, "settings_", "");
@@ -2255,7 +2256,7 @@ int main (int argc, char* argv[]) {
     { { "--name", "-n" }, true, true },
     { { "--vebose", "-V" }, true, false }
   };
-  createSubcommand("init", initOptions, false, [&](Map optionsWithValue, std::unordered_set optionsWithoutValue) -> void {
+  createSubcommand("init", initOptions, false, [&](Map<> optionsWithValue, std::unordered_set optionsWithoutValue) -> void {
     auto isTargetPathEmpty = fs::exists(targetPath) ? fs::is_empty(targetPath) : true;
     auto configOnly = optionsWithoutValue.find("--config") != optionsWithoutValue.end();
     auto projectName = optionsWithValue["--name"].size() > 0 ? optionsWithValue["--name"] : targetPath.filename().string();
@@ -2317,7 +2318,7 @@ int main (int argc, char* argv[]) {
     { { "--only" }, true, false },
     { { "--vebose", "-V" }, true, false }
   };
-  createSubcommand("list-devices", listDevicesOptions, false, [&](Map optionsWithValue, std::unordered_set optionsWithoutValue) -> void {
+  createSubcommand("list-devices", listDevicesOptions, false, [&](Map<> optionsWithValue, std::unordered_set optionsWithoutValue) -> void {
     bool isUdid =
       optionsWithoutValue.find("--udid") != optionsWithoutValue.end() ||
       equal(rc["list-devices_udid"], "true");
@@ -2425,7 +2426,8 @@ int main (int argc, char* argv[]) {
     { { "--verbose", "-V" }, true, false },
     { { "--target" }, true, true }
   };
-  createSubcommand("install-app", installAppOptions, true, [&](Map optionsWithValue, std::unordered_set optionsWithoutValue) -> void {
+
+  createSubcommand("install-app", installAppOptions, true, [&](Map<> optionsWithValue, std::unordered_set optionsWithoutValue) -> void {
     String commandOptions = "";
     String targetPlatform = optionsWithValue["--platform"];
     String installTarget = optionsWithValue["--target"];
@@ -2641,7 +2643,7 @@ int main (int argc, char* argv[]) {
     { { "--vebose", "-V" }, true, false }
   };
 
-  createSubcommand("print-build-dir", printBuildDirOptions, true, [&](Map optionsWithValue, std::unordered_set optionsWithoutValue) -> void {
+  createSubcommand("print-build-dir", printBuildDirOptions, true, [&](Map<> optionsWithValue, std::unordered_set optionsWithoutValue) -> void {
     bool flagRoot = optionsWithoutValue.find("--root") != optionsWithoutValue.end();
     // if --platform is specified, use the build path for the specified platform
     auto targetPlatform = optionsWithValue["--platform"];
@@ -2684,7 +2686,7 @@ int main (int argc, char* argv[]) {
 
   // Insert the elements of runOptions into buildOptions
   buildOptions.insert(buildOptions.end(), runOptions.begin(), runOptions.end());
-  createSubcommand("build", buildOptions, true, [&](Map optionsWithValue, std::unordered_set optionsWithoutValue) -> void {
+  createSubcommand("build", buildOptions, true, [&](Map<> optionsWithValue, std::unordered_set optionsWithoutValue) -> void {
     auto isForExtensionOnly = settings["meta_type"] == "extension" || settings["build_type"] == "extension";
 
     if (!isForExtensionOnly) {
@@ -3397,7 +3399,7 @@ int main (int argc, char* argv[]) {
         }
       }
 
-      Map manifestContext;
+      Map<> manifestContext;
 
       manifestContext["android_manifest_xml_permissions"] = "\n";
 
@@ -3571,7 +3573,7 @@ int main (int argc, char* argv[]) {
         << "-DSOCKET_RUNTIME_VERSION=" << SSC::VERSION_STRING << " "
         << "-DSOCKET_RUNTIME_VERSION_HASH=" << SSC::VERSION_HASH_STRING << " ";
 
-      Map makefileContext;
+      Map<> makefileContext;
 
       makefileContext["cflags"] = cflags + " " + pp.str();
       makefileContext["cflags"] += " " + settings["android_native_cflags"];
@@ -4238,8 +4240,8 @@ int main (int argc, char* argv[]) {
         settings["ini_code"]
       );
 
-      Map xCodeProjectVariables = settings;
-      extendMap(xCodeProjectVariables, Map {
+      Map<> xCodeProjectVariables = settings;
+      extendMap(xCodeProjectVariables, Map<> {
         {"SOCKET_RUNTIME_VERSION", VERSION_STRING},
         {"SOCKET_RUNTIME_VERSION_HASH", VERSION_HASH_STRING},
         {"SOCKET_RUNTIME_PLATFORM_SANDBOXED", flagCodeSign ? "1" : "0"},
@@ -5142,7 +5144,7 @@ int main (int argc, char* argv[]) {
 
       writeFile(pathBase / "LaunchScreen.storyboard", gStoryboardLaunchScreen);
 
-      Map entitlementSettings;
+      Map<> entitlementSettings;
       extendMap(entitlementSettings, settings);
 
       entitlementSettings["configured_entitlements"] = "";
@@ -6346,7 +6348,7 @@ int main (int argc, char* argv[]) {
       StringStream signCommand;
       String entitlements = "";
 
-      Map entitlementSettings;
+      Map<> entitlementSettings;
       extendMap(entitlementSettings, settings);
 
       entitlementSettings["configured_entitlements"] += (
@@ -7163,7 +7165,7 @@ int main (int argc, char* argv[]) {
     exit(exitCode);
   });
 
-  createSubcommand("run", runOptions, true, [&](Map optionsWithValue, std::unordered_set optionsWithoutValue) -> void {
+  createSubcommand("run", runOptions, true, [&](Map<> optionsWithValue, std::unordered_set optionsWithoutValue) -> void {
     String argvForward = "";
     bool flagRunHeadless = optionsWithoutValue.find("--headless") != optionsWithoutValue.end();
     bool flagTest = optionsWithoutValue.find("--test") != optionsWithoutValue.end() || optionsWithValue["--test"].size() > 0;
@@ -7265,7 +7267,8 @@ int main (int argc, char* argv[]) {
     { { "--debug", "-D" }, true, false },
     { { "--verbose", "-V" }, true, false },
   };
-  createSubcommand("setup", setupOptions, false, [&](Map optionsWithValue, std::unordered_set optionsWithoutValue) -> void {
+
+  createSubcommand("setup", setupOptions, false, [&](Map<> optionsWithValue, std::unordered_set optionsWithoutValue) -> void {
     auto help = false;
     auto yes = optionsWithoutValue.find("--yes") != optionsWithoutValue.end();
     String yesArg;
@@ -7360,8 +7363,8 @@ int main (int argc, char* argv[]) {
     exit(r);
   });
 
-  createSubcommand("env", {}, true, [&](Map optionsWithValue, std::unordered_set optionsWithoutValue) -> void {
-    auto envs = Map();
+  createSubcommand("env", {}, true, [&](Map<> optionsWithValue, std::unordered_set optionsWithoutValue) -> void {
+    auto envs = Map<>();
 
     envs["DEBUG"] = Env::get("DEBUG");
     envs["VERBOSE"] = Env::get("VERBOSE");
diff --git a/src/core/bluetooth.hh b/src/core/bluetooth.hh
deleted file mode 100644
index 073eb8b437..0000000000
--- a/src/core/bluetooth.hh
+++ /dev/null
@@ -1,72 +0,0 @@
-#ifndef SOCKET_RUNTIME_CORE_BLUETOOTH_H
-#define SOCKET_RUNTIME_CORE_BLUETOOTH_H
-
-#include "../platform/platform.hh"
-#include "json.hh"
-#include "post.hh"
-
-#if SOCKET_RUNTIME_PLATFORM_APPLE
-@interface SSCBluetoothController : NSObject<
-  CBCentralManagerDelegate,
-  CBPeripheralManagerDelegate,
-  CBPeripheralDelegate
->
-@property (strong, nonatomic) CBCentralManager* centralManager;
-@property (strong, nonatomic) CBPeripheralManager* peripheralManager;
-@property (strong, nonatomic) CBPeripheral* bluetoothPeripheral;
-@property (strong, nonatomic) NSMutableArray* peripherals;
-@property (strong, nonatomic) NSMutableDictionary* services;
-@property (strong, nonatomic) NSMutableDictionary* characteristics;
-@property (strong, nonatomic) NSMutableDictionary* serviceMap;
-- (void) startAdvertising;
-- (void) startScanning;
-- (id) init;
-@end
-#endif
-
-namespace SSC {
-  class Core;
-  class Bluetooth {
-    public:
-      using SendFunction = Function;
-      using EmitFunction = Function;
-      using Callback = Function;
-
-    #if SOCKET_RUNTIME_PLATFORM_APPLE
-      SSCBluetoothController* controller = nullptr;
-    #endif
-
-      Core *core = nullptr;
-      SendFunction sendFunction;
-      EmitFunction emitFunction;
-
-      Bluetooth ();
-      ~Bluetooth ();
-      bool send (const String& seq, JSON::Any json, Post post);
-      bool send (const String& seq, JSON::Any json);
-      bool emit (const String& seq, JSON::Any json);
-      void startScanning ();
-      void publishCharacteristic (
-        const String& seq,
-        char* bytes,
-        size_t size,
-        const String& serviceId,
-        const String& characteristicId,
-        Callback callback
-      );
-
-      void subscribeCharacteristic (
-        const String& seq,
-        const String& serviceId,
-        const String& characteristicId,
-        Callback callback
-      );
-
-      void startService (
-        const String& seq,
-        const String& serviceId,
-        Callback callback
-      );
-  };
-}
-#endif
diff --git a/src/core/codec.cc b/src/core/codec.cc
deleted file mode 100644
index 8ac058048d..0000000000
--- a/src/core/codec.cc
+++ /dev/null
@@ -1,427 +0,0 @@
-#include 
-
-#include "../platform/platform.hh"
-#include "codec.hh"
-
-#define UNSIGNED_IN_RANGE(value, min, max) (                                   \
-  (unsigned char) (value) >= (unsigned char) (min) &&                          \
-  (unsigned char) (value) <= (unsigned char) (max)                             \
-)
-
-static const char DEC2HEX[16 + 1] = "0123456789ABCDEF";
-
-//
-// All ipc uses a URI schema, so all ipc data needs to be
-// encoded as a URI component. This prevents escaping the
-// protocol.
-//
-static const signed char HEX2DEC[256] = {
-  /*       0  1  2  3   4  5  6  7   8  9  A  B   C  D  E  F */
-  /* 0 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-  /* 1 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-  /* 2 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-  /* 3 */  0, 1, 2, 3,  4, 5, 6, 7,  8, 9,-1,-1, -1,-1,-1,-1,
-
-  /* 4 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-  /* 5 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-  /* 6 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-  /* 7 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-
-  /* 8 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-  /* 9 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-  /* A */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-  /* B */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-
-  /* C */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-  /* D */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-  /* E */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-  /* F */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
-};
-
-static const char SAFE[256] = {
-  /*      0 1 2 3  4 5 6 7  8 9 A B  C D E F */
-  /* 0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
-  /* 1 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
-  /* 2 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
-  /* 3 */ 1,1,1,1, 1,1,1,1, 1,1,0,0, 0,0,0,0,
-
-  /* 4 */ 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
-  /* 5 */ 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0,
-  /* 6 */ 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
-  /* 7 */ 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0,
-
-  /* 8 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
-  /* 9 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
-  /* A */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
-  /* B */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
-
-  /* C */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
-  /* D */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
-  /* E */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
-  /* F */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0
-};
-
-// encoding table for base64 encoding
-static const char BASE64_ENCODING_TABLE[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-// modulus table for base64 encoding
-static const int BASE64_MOD_TABLE[] = {0, 2, 1};
-
-namespace SSC {
-  const Array toBytes (const uint64_t input) {
-    Array bytes;
-    // big endian, network order
-    bytes[0] = input >> 8*7;
-    bytes[1] = input >> 8*6;
-    bytes[2] = input >> 8*5;
-    bytes[3] = input >> 8*4;
-    bytes[4] = input >> 8*3;
-    bytes[5] = input >> 8*2;
-    bytes[6] = input >> 8*1;
-    bytes[7] = input >> 8*0;
-    return bytes;
-  }
-
-  void innerHash (unsigned int* result, unsigned int* w) {
-    unsigned int a = result[0];
-    unsigned int b = result[1];
-    unsigned int c = result[2];
-    unsigned int d = result[3];
-    unsigned int e = result[4];
-    int round = 0;
-
-    #define sha1macro(func,val) \
-      { \
-      const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \
-      e = d; \
-      d = c; \
-      c = rol(b, 30); \
-      b = a; \
-      a = t; \
-      }
-        while (round < 16) {
-            sha1macro((b & c) | (~b & d), 0x5a827999)
-            ++round;
-        }
-        while (round < 20) {
-            w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
-            sha1macro((b & c) | (~b & d), 0x5a827999)
-            ++round;
-        }
-        while (round < 40) {
-            w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
-            sha1macro(b ^ c ^ d, 0x6ed9eba1)
-            ++round;
-        }
-        while (round < 60) {
-            w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
-            sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc)
-            ++round;
-        }
-        while (round < 80) {
-            w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
-            sha1macro(b ^ c ^ d, 0xca62c1d6)
-            ++round;
-        }
-    #undef sha1macro
-
-    result[0] += a;
-    result[1] += b;
-    result[2] += c;
-    result[3] += d;
-    result[4] += e;
-  }
-
-  void shacalc (const char* src, char* dest) {
-    unsigned char hash[20];
-    int bytelength = strlen(src);
-    unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 };
-    const unsigned char* sarray = (const unsigned char*) src;
-    unsigned int w[80];
-    const int endOfFullBlocks = bytelength - 64;
-    int endCurrentBlock;
-    int currentBlock = 0;
-
-    while (currentBlock <= endOfFullBlocks) {
-      endCurrentBlock = currentBlock + 64;
-      int roundPos = 0;
-
-      for (roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4) {
-        w[roundPos++] = (unsigned int) sarray[currentBlock + 3]
-          | (((unsigned int) sarray[currentBlock + 2]) << 8)
-          | (((unsigned int) sarray[currentBlock + 1]) << 16)
-          | (((unsigned int) sarray[currentBlock]) << 24);
-      }
-      innerHash(result, w);
-    }
-
-    endCurrentBlock = bytelength - currentBlock;
-    clearWBuffert(w);
-    int lastBlockBytes = 0;
-
-    for (; lastBlockBytes < endCurrentBlock; ++lastBlockBytes) {
-      w[lastBlockBytes >> 2] |= (unsigned int) sarray[lastBlockBytes + currentBlock] << ((3 - (lastBlockBytes & 3)) << 3);
-    }
-
-    w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3);
-
-    if (endCurrentBlock >= 56) {
-      innerHash(result, w);
-      clearWBuffert(w);
-    }
-
-    w[15] = bytelength << 3;
-    innerHash(result, w);
-    int hashByte = 0;
-
-    for (hashByte = 20; --hashByte >= 0;) {
-      hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) & 0xff;
-    }
-    memcpy(dest, hash, 20);
-  }
-
-  const String shacalc (const String& input, size_t size) {
-    char output[size];
-    shacalc(input.data(), output);
-    return String(output, size);
-  }
-
-  size_t encodeBase64Length (size_t inputLength) {
-    return (size_t) (4.0 * ceil((double) inputLength / 3.0));
-  }
-
-  unsigned char* encodeBase64 (
-    const unsigned char* input,
-    unsigned char* output,
-    size_t inputLength,
-    size_t* outputLength
-  ) {
-    int i = 0;
-    int j = 0;
-
-    if (!output) {
-      return nullptr;
-    }
-
-    const auto length = encodeBase64Length(inputLength);
-
-    for (i = 0, j = 0; i < inputLength;) {
-      uint32_t octetA = i < inputLength ? input[i++] : 0;
-      uint32_t octetB = i < inputLength ? input[i++] : 0;
-      uint32_t octetC = i < inputLength ? input[i++] : 0;
-      uint32_t triple = (octetA << 0x10) + (octetB << 0x08) + octetC;
-      output[j++] = BASE64_ENCODING_TABLE[(triple >> 3 * 6) & 0x3F];
-      output[j++] = BASE64_ENCODING_TABLE[(triple >> 2 * 6) & 0x3F];
-      output[j++] = BASE64_ENCODING_TABLE[(triple >> 1 * 6) & 0x3F];
-      output[j++] = BASE64_ENCODING_TABLE[(triple >> 0 * 6) & 0x3F];
-    }
-
-    for (i = 0; i < BASE64_MOD_TABLE[inputLength % 3]; i++) {
-      output[length - 1 - i] = '=';
-    }
-
-    output[length] = 0;
-
-    if (outputLength != nullptr) {
-      *outputLength = length;
-    }
-
-    return output;
-  }
-
-  const Vector encodeBase64 (const String& input) {
-    size_t size;
-    unsigned char output[encodeBase64Length(input.size())];
-    encodeBase64(
-      reinterpret_cast(input.data()),
-      output,
-      input.size(),
-      &size
-    );
-
-    return Vector(output, output + size + 1);
-  }
-
-  String encodeURIComponent (const String& input) {
-    auto pointer = (unsigned char*) input.c_str();
-    const auto length = (int) input.length();
-    auto const start = new unsigned char[length* 3];
-    auto end = start;
-    const unsigned char* const maxLength = pointer + length;
-
-    for (; pointer < maxLength; ++pointer) {
-      if (SAFE[*pointer]) {
-        *end++ = *pointer;
-      } else {
-        // escape this char
-        *end++ = '%';
-        *end++ = DEC2HEX[*pointer >> 4];
-        *end++ = DEC2HEX[*pointer & 0x0F];
-      }
-    }
-
-    String result((char*) start, (char*) end);
-    delete [] start;
-    return result;
-  }
-
-  String decodeURIComponent (const String& input) {
-    // Note from RFC1630:  "Sequences which start with a percent sign
-    // but are not followed by two hexadecimal characters (0-9, A-F) are reserved
-    // for future extension"
-
-    const auto string = replace(input, "\\+", " ");
-    auto pointer = (const unsigned char *) string.c_str();
-    const auto length = (int) string.length();
-    const auto maxLength = pointer + length;
-
-    char* const start = new char[length];
-    char* end = start;
-
-    while (pointer < maxLength - 2) {
-      if (*pointer == '%') {
-        char dec1, dec2;
-        if (-1 != (dec1 = HEX2DEC[*(pointer + 1)])
-            && -1 != (dec2 = HEX2DEC[*(pointer + 2)])) {
-
-            *end++ = (dec1 << 4) + dec2;
-            pointer += 3;
-            continue;
-        }
-      }
-
-      *end++ = *pointer++;
-    }
-
-    // the last 2- chars
-    while (pointer < maxLength) {
-      *end++ = *pointer++;
-    }
-
-    String result(start, end);
-    delete [] start;
-    return result;
-  }
-
-  String encodeHexString (const String& input) {
-    String output;
-    auto length = 2 * input.length();
-
-    output.reserve(length);
-
-    for (unsigned char character : input) {
-      output.push_back(DEC2HEX[character >> 4]);
-      output.push_back(DEC2HEX[character & 15]);
-    }
-
-    return output;
-  }
-
-  String decodeHexString (const String& input) {
-    const auto length = input.length() / 2;
-    String output;
-
-    output.reserve(length);
-
-    for (auto character = input.begin(); character != input.end();) {
-      const int hi = HEX2DEC[(unsigned char) *character++];
-      const int lo = HEX2DEC[(unsigned char) *character++];
-      output.push_back(hi << 4 | lo);
-    }
-
-    return output;
-  }
-
-  size_t decodeUTF8 (char *output, const char *input, size_t length) {
-    unsigned char cp = 0; // code point
-    unsigned char lower = 0x80;
-    unsigned char upper = 0xBF;
-
-    int x = 0; // cp needed
-    int y = 0; // cp  seen
-    int size = 0; // output size
-
-    for (int i = 0; i < length; ++i) {
-      auto b = (unsigned char) input[i];
-
-      if (b == 0) {
-        output[size++] = 0;
-        continue;
-      }
-
-      if (x == 0) {
-        // 1 byte
-        if (UNSIGNED_IN_RANGE(b, 0x00, 0x7F)) {
-          output[size++] = b;
-          continue;
-        }
-
-        if (!UNSIGNED_IN_RANGE(b, 0xC2, 0xF4)) {
-          break;
-        }
-
-        // 2 byte
-        if (UNSIGNED_IN_RANGE(b, 0xC2, 0xDF)) {
-          x = 1;
-          cp = b - 0xC0;
-        }
-
-        // 3 byte
-        if (UNSIGNED_IN_RANGE(b, 0xE0, 0xEF)) {
-          if (b == 0xE0) {
-            lower = 0xA0;
-          } else if (b == 0xED) {
-            upper = 0x9F;
-          }
-
-          x = 2;
-          cp = b - 0xE0;
-        }
-
-        // 4 byte
-        if (UNSIGNED_IN_RANGE(b, 0xF0, 0xF4)) {
-          if (b == 0xF0) {
-            lower = 0x90;
-          } else if (b == 0xF4) {
-            upper = 0x8F;
-          }
-
-          x = 3;
-          cp = b - 0xF0;
-        }
-
-        cp = cp * pow(64, x);
-        continue;
-      }
-
-      if (!UNSIGNED_IN_RANGE(b, lower, upper)) {
-        lower = 0x80;
-        upper = 0xBF;
-
-        // revert
-        cp = 0;
-        x = 0;
-        y = 0;
-        i--;
-        continue;
-      }
-
-      lower = 0x80;
-      upper = 0xBF;
-      y++;
-      cp += (b - 0x80) * pow(64, x - y);
-
-      if (y != x) {
-        continue;
-      }
-
-      output[size++] = cp;
-      // continue to next
-      cp = 0;
-      x = 0;
-      y = 0;
-    }
-
-    return size;
-  }
-}
diff --git a/src/core/codec.hh b/src/core/codec.hh
deleted file mode 100644
index cfada68a59..0000000000
--- a/src/core/codec.hh
+++ /dev/null
@@ -1,112 +0,0 @@
-#ifndef SOCKET_RUNTIME_CORE_CODEC_H
-#define SOCKET_RUNTIME_CORE_CODEC_H
-
-#include "../platform/types.hh"
-
-#define SHA_DIGEST_LENGTH 20
-
-namespace SSC {
-  /**
-   * Encodes input by replacing certain characters by
-   * one, two, three, or four escape sequences representing the UTF-8
-   * encoding of the character.
-   * @param input The input string to encode
-   * @return An encoded string value
-   */
-  String encodeURIComponent (const String& input);
-
-  /**
-   * Decodes a value encoded with `encodeURIComponent`
-   * @param input The input string to decode
-   * @return A decoded string
-   */
-  String decodeURIComponent (const String& input);
-
-  /**
-   * Encodes input as a string of hex characters.
-   * @param input The input string to encode
-   * @return An encoded string value
-   */
-  String encodeHexString (const String& input);
-
-  /**
-   * Decodes input as a string of hex characters to a normal string.
-   * @param input The input string to encode
-   * @return An encoded string value
-   */
-  String decodeHexString (const String& input);
-
-  /**
-   * Decodes UTF8 byte string of variable `length` size in `input` to
-   * `output` returning `size_t` bytes written to `output`.
-   * @param output Pointer owned by caller to write decoded output to
-   * @param input Pointer owned by caller to decode `length` bytes
-   * @param length Size of `input` in bytes
-   * @return The number of bytes written to `output`
-   */
-  size_t decodeUTF8 (char *output, const char *input, size_t length);
-
-  /**
-   * Converts a `uint64_t` to a array of `uint8_t` values (bytes)
-   * @param input The `uint64_t` to convert to an array of bytes
-   * @return An array of `uint8_t` values
-   */
-  const Array toBytes (const uint64_t input);
-
-  /**
-   * Rotates an unsigned integer value left by a specified number of steps.
-   * @param value The unsigned integer value to rotate
-   * @param steps The number of steps to rotate left
-   * @return The rotated unsigned integer value
-   */
-  inline const unsigned int rol (const unsigned int value, const unsigned int steps) {
-    return ((value << steps) | (value >> (32 - steps)));
-  }
-
-  /**
-   * Clears a buffer of unsigned integers by setting each element to zero.
-   * @param buffert Pointer to the buffer to clear
-   */
-  inline void clearWBuffert (unsigned int* buffert) {
-    int pos = 0;
-    for (pos = 16; --pos >= 0;) {
-      buffert[pos] = 0;
-    }
-  }
-
-  /**
-   * Computes the inner hash for the SHA-1 algorithm.
-   * @param result Pointer to an array of unsigned integers to store the result
-   * @param w Pointer to an array of unsigned integers to use in computation
-   */
-  void innerHash (unsigned int* result, unsigned int* w);
-
-  /**
-   * Calculates the SHA-1 hash of a given input string.
-   * @param src Pointer to the input string
-   * @param dest Pointer to the destination buffer to store the hash
-   */
-  void shacalc (const char* src, char* dest);
-  const String shacalc (const String&, size_t size = SHA_DIGEST_LENGTH);
-
-  /**
-   * Encodes a given input data to a Base64 encoded string.
-   * @param input Pointer to the input data
-   * @param output Pointer to the output data
-   * @param inputLength Length of the input data
-   * @param outputLength Pointer to store the length of the encoded output
-   * @return Pointer to the Base64 encoded string
-   */
-  unsigned char* encodeBase64 (
-    const unsigned char* input,
-    unsigned char* output,
-    size_t inputLength,
-    size_t* outputLength
-  );
-
-  size_t encodeBase64Length (size_t inputLength);
-
-  const Vector encodeBase64 (const String&);
-}
-
-#endif
diff --git a/src/core/core.cc b/src/core/core.cc
deleted file mode 100644
index f864727bd4..0000000000
--- a/src/core/core.cc
+++ /dev/null
@@ -1,721 +0,0 @@
-#include "core.hh"
-#include "modules/fs.hh"
-
-namespace SSC {
-#if SOCKET_RUNTIME_PLATFORM_LINUX
-  struct UVSource {
-    GSource base; // should ALWAYS be first member
-    gpointer tag;
-    Core *core;
-  };
-#endif
-
-  Post Core::getPost (uint64_t id) {
-    if (this->posts.find(id) == this->posts.end()) {
-      return Post{};
-    }
-
-    return posts.at(id);
-  }
-
-  void Core::shutdown () {
-    if (this->isShuttingDown || this->isPaused) {
-      return;
-    }
-
-    this->isShuttingDown = true;
-    this->pause();
-
-  #if !SOCKET_RUNTIME_PLATFORM_IOS
-    this->childProcess.shutdown();
-  #endif
-
-    this->stopEventLoop();
-    this->isShuttingDown = false;
-  }
-
-  void Core::resume () {
-    if (!this->isPaused) {
-      return;
-    }
-
-    this->isPaused = false;
-    this->runEventLoop();
-
-    if (this->options.features.useUDP) {
-      this->udp.resumeAllSockets();
-    }
-
-    if (options.features.useNetworkStatus) {
-      this->networkStatus.start();
-    }
-
-    if (options.features.useConduit) {
-      this->conduit.start();
-    }
-
-    if (options.features.useNotifications) {
-      this->notifications.start();
-    }
-  }
-
-  void Core::pause () {
-    if (this->isPaused) {
-      return;
-    }
-
-    this->isPaused = true;
-
-    if (this->options.features.useUDP) {
-      this->udp.pauseAllSockets();
-    }
-
-    if (options.features.useNetworkStatus) {
-      this->networkStatus.stop();
-    }
-
-    if (options.features.useConduit) {
-      this->conduit.stop();
-    }
-
-    if (options.features.useNotifications) {
-      this->notifications.stop();
-    }
-
-  #if !SOCKET_RUNTIME_PLATFORM_ANDROID
-    this->pauseEventLoop();
-  #endif
-  }
-
-  void Core::stop () {
-    Lock lock(this->mutex);
-    this->stopEventLoop();
-  #if SOCKET_RUNTIME_PLATFORM_LINUX
-    if (this->gsource) {
-      const auto id = g_source_get_id(this->gsource);
-      if (id > 0) {
-        g_source_remove(id);
-      }
-
-      g_object_unref(this->gsource);
-      this->gsource = nullptr;
-      this->didInitGSource = false;
-    }
-  #endif
-  }
-
-  bool Core::hasPost (uint64_t id) {
-    return posts.find(id) != posts.end();
-  }
-
-  bool Core::hasPostBody (const char* body) {
-    if (body == nullptr) return false;
-    for (const auto& tuple : posts) {
-      auto post = tuple.second;
-      if (post.body.get() == body) return true;
-    }
-    return false;
-  }
-
-  void Core::expirePosts () {
-    Lock lock(this->mutex);
-    const auto now = std::chrono::system_clock::now()
-      .time_since_epoch()
-      .count();
-
-    Vector ids;
-    for (auto const &tuple : posts) {
-      auto id = tuple.first;
-      auto post = tuple.second;
-
-      if (post.ttl < now) {
-        ids.push_back(id);
-      }
-    }
-
-    for (auto const id : ids) {
-      removePost(id);
-    }
-  }
-
-  void Core::putPost (uint64_t id, Post p) {
-    Lock lock(this->mutex);
-    p.ttl = std::chrono::time_point_cast(
-      std::chrono::system_clock::now() +
-      std::chrono::milliseconds(32 * 1024)
-    )
-      .time_since_epoch()
-      .count();
-
-    this->posts.insert_or_assign(id, p);
-  }
-
-  void Core::removePost (uint64_t id) {
-    Lock lock(this->mutex);
-
-    if (this->posts.find(id) != this->posts.end()) {
-      // debug("remove post %ld", this->posts.at(id).body.use_count());
-      posts.erase(id);
-    }
-  }
-
-  String Core::createPost (String seq, String params, Post post) {
-    if (post.id == 0) {
-      post.id = rand64();
-    }
-
-    auto sid = std::to_string(post.id);
-    auto js = createJavaScript("post-data.js",
-      "const globals = await import('socket:internal/globals');              \n"
-      "const id = `" + sid + "`;                                             \n"
-      "const seq = `" + seq + "`;                                            \n"
-      "const workerId = `" + post.workerId + "`.trim() || null;              \n"
-      "const headers = `" + trim(post.headers) + "`                          \n"
-      "  .trim()                                                             \n"
-      "  .split(/[\\r\\n]+/)                                                 \n"
-      "  .filter(Boolean)                                                    \n"
-      "  .map((header) => header.trim());                                    \n"
-      "                                                                      \n"
-      "let params = `" + params + "`;                                        \n"
-      "                                                                      \n"
-      "try {                                                                 \n"
-      "  params = JSON.parse(params);                                        \n"
-      "} catch (err) {                                                       \n"
-      "  console.error(err.stack || err, params);                            \n"
-      "}                                                                     \n"
-      "                                                                      \n"
-      "globals.get('RuntimeXHRPostQueue').dispatch(                          \n"
-      "  id,                                                                 \n"
-      "  seq,                                                                \n"
-      "  params,                                                             \n"
-      "  headers,                                                            \n"
-      "  { workerId }                                                        \n"
-      ");                                                                    \n"
-    );
-
-    putPost(post.id, post);
-    return js;
-  }
-
-  void Core::removeAllPosts () {
-    Lock lock(this->mutex);
-    Vector ids;
-
-    for (auto const &tuple : posts) {
-      auto id = tuple.first;
-      ids.push_back(id);
-    }
-
-    for (auto const id : ids) {
-      removePost(id);
-    }
-  }
-
-#if SOCKET_RUNTIME_PLATFORM_LINUX
-  // @see https://api.gtkd.org/glib.c.types.GSourceFuncs.html
-  static GSourceFuncs loopSourceFunctions = {
-    .prepare = [](GSource *source, gint *timeout) -> gboolean {
-      auto core = reinterpret_cast(source)->core;
-      if (!core->isLoopRunning) {
-        return false;
-      }
-
-      if (!core->isLoopAlive()) {
-        return true;
-      }
-
-      *timeout = core->getEventLoopTimeout();
-      return *timeout == 0;
-    },
-
-    .check = [](GSource* source) -> gboolean {
-      auto core = reinterpret_cast(source)->core;
-      auto tag = reinterpret_cast(source)->tag;
-      const auto timeout = core->getEventLoopTimeout();
-
-      if (timeout == 0) {
-        return true;
-      }
-
-      const auto condition = g_source_query_unix_fd(source, tag);
-      return (
-        ((condition & G_IO_IN) == G_IO_IN) ||
-        ((condition & G_IO_OUT) == G_IO_OUT)
-      );
-    },
-
-    .dispatch = [](
-      GSource *source,
-      GSourceFunc callback,
-      gpointer user_data
-    ) -> gboolean {
-      auto core = reinterpret_cast(source)->core;
-      auto loop = core->getEventLoop();
-      uv_run(loop, UV_RUN_NOWAIT);
-      return G_SOURCE_CONTINUE;
-    }
-  };
-#endif
-
-  void Core::initEventLoop () {
-    if (didLoopInit) {
-      return;
-    }
-
-    didLoopInit = true;
-    Lock lock(this->mutex);
-    uv_loop_init(&this->eventLoop);
-    uv_loop_set_data(&this->eventLoop, reinterpret_cast(this));
-    this->eventLoopAsync.data = reinterpret_cast(this);
-
-    uv_async_init(&this->eventLoop, &this->eventLoopAsync, [](uv_async_t *handle) {
-      auto core = reinterpret_cast(handle->data);
-
-      while (true) {
-        Function dispatch = nullptr;
-
-        do {
-          Lock lock(core->mutex);
-          if (core->eventLoopDispatchQueue.size() > 0) {
-            dispatch = core->eventLoopDispatchQueue.front();
-            core->eventLoopDispatchQueue.pop();
-          }
-        } while (0);
-
-        if (dispatch == nullptr) {
-          break;
-        }
-
-        dispatch();
-      }
-    });
-
-  #if SOCKET_RUNTIME_PLATFORM_LINUX
-    if (!this->options.dedicatedLoopThread && !this->didInitGSource) {
-      if (this->gsource) {
-        const auto id = g_source_get_id(this->gsource);
-        if (id > 0) {
-          g_source_remove(id);
-        }
-
-        g_object_unref(this->gsource);
-        this->gsource = nullptr;
-      }
-
-      this->gsource = g_source_new(&loopSourceFunctions, sizeof(UVSource));
-
-      UVSource *uvsource = reinterpret_cast(gsource);
-      uvsource->core = this;
-      uvsource->tag = g_source_add_unix_fd(
-        this->gsource,
-        uv_backend_fd(&this->eventLoop),
-        (GIOCondition) (G_IO_IN | G_IO_OUT | G_IO_ERR)
-      );
-
-      g_source_set_priority(this->gsource, G_PRIORITY_HIGH);
-      g_source_attach(this->gsource, nullptr);
-      this->didInitGSource = true;
-    }
-  #endif
-  }
-
-  uv_loop_t* Core::getEventLoop () {
-    this->initEventLoop();
-    return &this->eventLoop;
-  }
-
-  int Core::getEventLoopTimeout () {
-    auto loop = this->getEventLoop();
-    uv_update_time(loop);
-    return uv_backend_timeout(loop);
-  }
-
-  bool Core::isLoopAlive () {
-    return uv_loop_alive(this->getEventLoop());
-  }
-
-  void Core::pauseEventLoop() {
-  #if !SOCKET_RUNTIME_PLATFORM_LINUX
-    // wait for drain of event loop dispatch queue
-    while (true) {
-      Lock lock(this->mutex);
-      if (this->eventLoopDispatchQueue.size() == 0) {
-        break;
-      }
-    }
-  #endif
-
-    this->isLoopRunning = false;
-    do {
-      Lock lock(this->mutex);
-      uv_stop(&this->eventLoop);
-    } while (0);
-
-  #if !SOCKET_RUNTIME_PLATFORM_APPLE
-    #if SOCKET_RUNTIME_PLATFORM_LINUX
-      if (this->options.dedicatedLoopThread) {
-    #endif
-      if (this->eventLoopThread != nullptr) {
-        if (this->isPollingEventLoop && eventLoopThread->joinable()) {
-          this->eventLoopThread->join();
-        }
-
-        delete this->eventLoopThread;
-        this->eventLoopThread = nullptr;
-      }
-    #if SOCKET_RUNTIME_PLATFORM_LINUX
-      }
-    #endif
-  #endif
-  }
-
-  void Core::stopEventLoop() {
-    if (this->isLoopRunning) {
-      return;
-    }
-
-    this->isLoopRunning = false;
-    Lock lock(this->mutex);
-    uv_stop(&eventLoop);
-  #if !SOCKET_RUNTIME_PLATFORM_APPLE
-    #if SOCKET_RUNTIME_PLATFORM_LINUX
-      if (this->options.dedicatedLoopThread) {
-    #endif
-      if (eventLoopThread != nullptr) {
-        if (this->isPollingEventLoop && eventLoopThread->joinable()) {
-          eventLoopThread->join();
-        }
-
-        delete eventLoopThread;
-        eventLoopThread = nullptr;
-      }
-    #if SOCKET_RUNTIME_PLATFORM_LINUX
-      }
-    #endif
-  #endif
-
-    uv_loop_close(&eventLoop);
-  }
-
-  void Core::sleepEventLoop (int64_t ms) {
-    if (ms > 0) {
-      auto timeout = this->getEventLoopTimeout();
-      ms = timeout > ms ? timeout : ms;
-      msleep(ms);
-    }
-  }
-
-  void Core::sleepEventLoop () {
-    this->sleepEventLoop(this->getEventLoopTimeout());
-  }
-
-  void Core::signalDispatchEventLoop () {
-    Lock lock(this->mutex);
-    this->initEventLoop();
-    this->runEventLoop();
-    uv_async_send(&this->eventLoopAsync);
-  }
-
-  void Core::dispatchEventLoop (EventLoopDispatchCallback callback) {
-    {
-      Lock lock(this->mutex);
-      this->eventLoopDispatchQueue.push(callback);
-    }
-
-    this->signalDispatchEventLoop();
-  }
-
-  static void pollEventLoop (Core *core) {
-    core->isPollingEventLoop = true;
-    auto loop = core->getEventLoop();
-
-    while (core->isLoopRunning) {
-      core->sleepEventLoop(EVENT_LOOP_POLL_TIMEOUT);
-
-      do {
-        uv_run(loop, UV_RUN_DEFAULT);
-      } while (core->isLoopRunning && core->isLoopAlive());
-    }
-
-    core->isPollingEventLoop = false;
-    core->isLoopRunning = false;
-  }
-
-  void Core::runEventLoop () {
-    if (
-      this->isShuttingDown ||
-      this->isLoopRunning ||
-      this->isPaused
-    ) {
-      return;
-    }
-
-    this->isLoopRunning = true;
-    this->isPaused = false;
-
-    this->initEventLoop();
-    this->dispatchEventLoop([=, this]() {
-      this->initTimers();
-      this->startTimers();
-    });
-
-  #if SOCKET_RUNTIME_PLATFORM_APPLE
-    Lock lock(this->mutex);
-    dispatch_async(this->eventLoopQueue, ^{
-      pollEventLoop(this);
-    });
-  #else
-  #if SOCKET_RUNTIME_PLATFORM_LINUX
-    if (this->options.dedicatedLoopThread) {
-  #endif
-    Lock lock(this->mutex);
-    // clean up old thread if still running
-    if (this->eventLoopThread != nullptr) {
-      if (!this->isPollingEventLoop && this->eventLoopThread->joinable()) {
-        this->eventLoopThread->join();
-      }
-
-      delete this->eventLoopThread;
-      this->eventLoopThread = nullptr;
-    }
-
-    this->eventLoopThread = new std::thread(
-      &pollEventLoop,
-      this
-    );
-  #if SOCKET_RUNTIME_PLATFORM_LINUX
-    }
-  #endif
-  #endif
-  }
-
-  struct Timer {
-    uv_timer_t handle;
-    bool repeated = false;
-    bool started = false;
-    uint64_t timeout = 0;
-    uint64_t interval = 0;
-    uv_timer_cb invoke;
-  };
-
-  static Timer releaseStrongReferenceDescriptors = {
-    .repeated = true,
-    .timeout = 1024, // in milliseconds
-    .invoke = [](uv_timer_t *handle) {
-      auto core = reinterpret_cast(handle->data);
-      Vector ids;
-      String msg = "";
-
-      {
-        Lock lock(core->fs.mutex);
-        for (auto const &tuple : core->fs.descriptors) {
-          ids.push_back(tuple.first);
-        }
-      }
-
-      for (auto const id : ids) {
-        Lock lock(core->fs.mutex);
-        if (!core->fs.descriptors.contains(id)) {
-          continue;
-        }
-
-        auto desc = core->fs.descriptors.at(id);
-
-        if (desc == nullptr) {
-          core->fs.descriptors.erase(id);
-          continue;
-        }
-
-        if (desc->isRetained() || !desc->isStale()) {
-          continue;
-        }
-
-        if (desc->isDirectory()) {
-          core->fs.closedir("", id, [](auto seq, auto msg, auto post) {});
-        } else if (desc->isFile()) {
-          core->fs.close("", id, [](auto seq, auto msg, auto post) {});
-        } else {
-          // free
-          core->fs.descriptors.erase(id);
-        }
-      }
-    }
-  };
-
-  #define RELEASE_STRONG_REFERENCE_SHARED_POINTER_BUFFERS_RESOLUTION 8
-
-  static Timer releaseStrongReferenceSharedPointerBuffers = {
-    .repeated = true,
-    .timeout = RELEASE_STRONG_REFERENCE_SHARED_POINTER_BUFFERS_RESOLUTION, // in milliseconds
-    .invoke = [](uv_timer_t *handle) {
-      auto core = reinterpret_cast(handle->data);
-      static constexpr auto resolution = RELEASE_STRONG_REFERENCE_SHARED_POINTER_BUFFERS_RESOLUTION;
-      Lock lock(core->mutex);
-      for (int i = 0; i < core->sharedPointerBuffers.size(); ++i) {
-        auto entry = &core->sharedPointerBuffers[i];
-        if (entry == nullptr) {
-          continue;
-        }
-
-        // expired
-        if (entry->ttl <= resolution) {
-          entry->pointer = nullptr;
-          entry->ttl = 0;
-          if (i == core->sharedPointerBuffers.size() - 1) {
-            core->sharedPointerBuffers.pop_back();
-            break;
-          }
-        } else {
-          entry->ttl = entry->ttl - resolution;
-        }
-      }
-
-      while (
-        core->sharedPointerBuffers.size() > 0 &&
-        core->sharedPointerBuffers.back().pointer == nullptr
-      ) {
-        core->sharedPointerBuffers.pop_back();
-      }
-
-      if (core->sharedPointerBuffers.size() == 0) {
-        uv_timer_stop(&releaseStrongReferenceSharedPointerBuffers.handle);
-      }
-    }
-  };
-
-  void Core::initTimers () {
-    if (this->didTimersInit) {
-      return;
-    }
-
-    Lock lock(this->mutex);
-
-    auto loop = this->getEventLoop();
-
-    Vector timersToInit = {
-      &releaseStrongReferenceDescriptors,
-      &releaseStrongReferenceSharedPointerBuffers
-    };
-
-    for (const auto& timer : timersToInit) {
-      uv_timer_init(loop, &timer->handle);
-      timer->handle.data = (void *) this;
-    }
-
-    this->didTimersInit = true;
-  }
-
-  void Core::startTimers () {
-    Lock lock(this->mutex);
-
-    Vector timersToStart = {
-      &releaseStrongReferenceDescriptors,
-      &releaseStrongReferenceSharedPointerBuffers
-    };
-
-    for (const auto &timer : timersToStart) {
-      if (timer->started) {
-        uv_timer_again(&timer->handle);
-      } else {
-        timer->started = 0 == uv_timer_start(
-          &timer->handle,
-          timer->invoke,
-          timer->timeout,
-          !timer->repeated
-            ? 0
-            : timer->interval > 0
-              ? timer->interval
-              : timer->timeout
-        );
-      }
-    }
-
-    this->didTimersStart = true;
-  }
-
-  void Core::stopTimers () {
-    if (this->didTimersStart == false) {
-      return;
-    }
-
-    Lock lock(this->mutex);
-
-    Vector timersToStop = {
-      &releaseStrongReferenceDescriptors,
-      &releaseStrongReferenceSharedPointerBuffers
-    };
-
-    for (const auto& timer : timersToStop) {
-      if (timer->started) {
-        uv_timer_stop(&timer->handle);
-      }
-    }
-
-    this->didTimersStart = false;
-  }
-
-  const CoreTimers::ID Core::setTimeout (
-    uint64_t timeout,
-    const CoreTimers::TimeoutCallback& callback
-  ) {
-    return this->timers.setTimeout(timeout, callback);
-  }
-
-  const CoreTimers::ID Core::setImmediate (
-    const CoreTimers::ImmediateCallback& callback
-  ) {
-    return this->timers.setImmediate(callback);
-  }
-
-  const CoreTimers::ID Core::setInterval (
-    uint64_t interval,
-    const CoreTimers::IntervalCallback& callback
-  ) {
-    return this->timers.setInterval(interval, callback);
-  }
-
-  bool Core::clearTimeout (const CoreTimers::ID id) {
-    return this->timers.clearTimeout(id);
-  }
-
-  bool Core::clearImmediate (const CoreTimers::ID id) {
-    return this->timers.clearImmediate(id);
-  }
-
-  bool Core::clearInterval (const CoreTimers::ID id) {
-    return this->timers.clearInterval(id);
-  }
-
-  void Core::retainSharedPointerBuffer (
-    SharedPointer pointer,
-    unsigned int ttl
-  ) {
-    if (pointer == nullptr) {
-      return;
-    }
-
-    Lock lock(this->mutex);
-
-    this->sharedPointerBuffers.emplace_back(SharedPointerBuffer {
-      pointer,
-      ttl
-    });
-
-    uv_timer_again(&releaseStrongReferenceSharedPointerBuffers.handle);
-  }
-
-  void Core::releaseSharedPointerBuffer (SharedPointer pointer) {
-    if (pointer == nullptr) {
-      return;
-    }
-
-    Lock lock(this->mutex);
-    for (auto& entry : this->sharedPointerBuffers) {
-      if (entry.pointer.get() == pointer.get()) {
-        entry.pointer = nullptr;
-        entry.ttl = 0;
-        return;
-      }
-    }
-  }
-}
diff --git a/src/core/core.hh b/src/core/core.hh
deleted file mode 100644
index f33ebfcb4a..0000000000
--- a/src/core/core.hh
+++ /dev/null
@@ -1,276 +0,0 @@
-#ifndef SOCKET_RUNTIME_CORE_CORE_H
-#define SOCKET_RUNTIME_CORE_CORE_H
-
-#include "../platform/platform.hh"
-
-#include "bluetooth.hh"
-#include "codec.hh"
-#include "color.hh"
-#include "config.hh"
-#include "debug.hh"
-#include "env.hh"
-#include "file_system_watcher.hh"
-#include "headers.hh"
-#include "ini.hh"
-#include "io.hh"
-#include "ip.hh"
-#include "json.hh"
-#include "module.hh"
-#include "options.hh"
-#include "post.hh"
-#include "resource.hh"
-#include "socket.hh"
-#include "unique_client.hh"
-#include "url.hh"
-#include "version.hh"
-#include "webview.hh"
-
-#include "modules/ai.hh"
-#include "modules/child_process.hh"
-#include "modules/conduit.hh"
-#include "modules/diagnostics.hh"
-#include "modules/dns.hh"
-#include "modules/fs.hh"
-#include "modules/geolocation.hh"
-#include "modules/media_devices.hh"
-#include "modules/network_status.hh"
-#include "modules/notifications.hh"
-#include "modules/os.hh"
-#include "modules/permissions.hh"
-#include "modules/platform.hh"
-#include "modules/timers.hh"
-#include "modules/udp.hh"
-
-namespace SSC {
-  constexpr int EVENT_LOOP_POLL_TIMEOUT = 32; // in milliseconds
-
-  // forward
-  class Core;
-  class Process;
-
-  using EventLoopDispatchCallback = Function;
-
-  class Core {
-    public:
-    #if !SOCKET_RUNTIME_PLATFORM_IOS
-      using ChildProcess = CoreChildProcess;
-    #endif
-      using DNS = CoreDNS;
-      using Diagnostics = CoreDiagnostics;
-      using FS = CoreFS;
-      using Conduit = CoreConduit;
-      using Geolocation = CoreGeolocation;
-      using MediaDevices = CoreMediaDevices;
-      using NetworkStatus = CoreNetworkStatus;
-      using Notifications = CoreNotifications;
-      using OS = CoreOS;
-      using Permissions = CorePermissions;
-      using Platform = CorePlatform;
-      using Timers = CoreTimers;
-      using UDP = CoreUDP;
-      using AI = CoreAI;
-
-      struct Options : SSC::Options {
-        struct Features {
-          #if !SOCKET_RUNTIME_PLATFORM_IOS
-            bool useChildProcess = true;
-          #endif
-
-          bool useDNS = true;
-          bool useFS = true;
-          bool useGeolocation = true;
-          bool useNetworkStatus = true;
-          bool useNotifications = true;
-          bool useConduit = true;
-          bool useOS = true;
-          bool usePermissions = true;
-          bool usePlatform = true;
-          bool useTimers = true;
-          bool useUDP = true;
-          bool useAI = true;
-        };
-
-        Features features;
-
-        #if SOCKET_RUNTIME_PLATFORM_LINUX
-          // this is turned on in the WebKitWebProcess extension to avoid
-          // deadlocking the GTK loop AND WebKit WebView thread as they
-          // are shared and we typically "interpolate" loop execution
-          // with the GTK thread on the main runtime process
-          bool dedicatedLoopThread = false;
-        #endif
-      };
-
-      struct SharedPointerBuffer {
-        SharedPointer pointer;
-        unsigned int ttl = 0;
-      };
-
-      #if !SOCKET_RUNTIME_PLATFORM_IOS
-        ChildProcess childProcess;
-      #endif
-      Diagnostics diagnostics;
-      DNS dns;
-      FS fs;
-      Conduit conduit;
-      Geolocation geolocation;
-      MediaDevices mediaDevices;
-      NetworkStatus networkStatus;
-      Notifications notifications;
-      OS os;
-      Permissions permissions;
-      Platform platform;
-      Timers timers;
-      UDP udp;
-      AI ai;
-
-      Vector sharedPointerBuffers;
-      Options options = {};
-      Posts posts;
-
-      Mutex mutex;
-
-      Atomic didLoopInit = false;
-      Atomic didTimersInit = false;
-      Atomic didTimersStart = false;
-
-      Atomic isPollingEventLoop = false;
-      Atomic isShuttingDown = false;
-      Atomic isLoopRunning = false;
-      Atomic isPaused = true;
-
-      uv_loop_t eventLoop;
-      uv_async_t eventLoopAsync;
-      Queue eventLoopDispatchQueue;
-
-      #if SOCKET_RUNTIME_PLATFORM_APPLE
-        dispatch_queue_attr_t eventLoopQueueAttrs = dispatch_queue_attr_make_with_qos_class(
-          DISPATCH_QUEUE_SERIAL,
-          QOS_CLASS_DEFAULT,
-          -1
-        );
-
-        dispatch_queue_t eventLoopQueue = dispatch_queue_create(
-          "socket.runtime.core.loop.queue",
-          eventLoopQueueAttrs
-        );
-      #else
-        Thread *eventLoopThread = nullptr;
-      #endif
-
-      #if SOCKET_RUNTIME_PLATFORM_LINUX
-        Atomic didInitGSource = false;
-        GSource* gsource = nullptr;
-      #endif
-
-      Core (const Options& options) :
-        options(options),
-        #if !SOCKET_RUNTIME_PLATFORM_IOS
-          childProcess(this),
-        #endif
-        ai(this),
-        diagnostics(this),
-        conduit(this),
-        dns(this),
-        fs(this),
-        geolocation(this),
-        mediaDevices(this),
-        networkStatus(this),
-        notifications(this),
-        os(this),
-        permissions(this),
-        platform(this),
-        timers(this),
-        udp(this)
-      {
-        this->initEventLoop();
-        this->resume();
-      }
-
-      Core ()
-        : Core(Options {})
-      {}
-
-      ~Core () {
-        this->shutdown();
-      }
-
-      // called when the application is shutting down
-      void shutdown ();
-      void resume ();
-      void pause ();
-      void stop ();
-
-      int logSeq{0};
-
-      void retainSharedPointerBuffer (SharedPointer pointer, unsigned int ttl);
-      void releaseSharedPointerBuffer (SharedPointer pointer);
-
-      // core module post data management
-      Post getPost (uint64_t id);
-      bool hasPost (uint64_t id);
-      bool hasPostBody (const char* body);
-      void removePost (uint64_t id);
-      void removeAllPosts ();
-      void expirePosts ();
-      void putPost (uint64_t id, Post p);
-      String createPost (String seq, String params, Post post);
-
-      // timers
-      void initTimers ();
-      void startTimers ();
-      void stopTimers ();
-      const CoreTimers::ID setTimeout (uint64_t timeout, const CoreTimers::TimeoutCallback& callback);
-      const CoreTimers::ID setInterval (uint64_t interval, const CoreTimers::IntervalCallback& callback);
-      const CoreTimers::ID setImmediate (const CoreTimers::ImmediateCallback& callback);
-      bool clearTimeout (const CoreTimers::ID id);
-      bool clearInterval (const CoreTimers::ID id);
-      bool clearImmediate (const CoreTimers::ID id);
-
-      // loop
-      uv_loop_t* getEventLoop ();
-      int getEventLoopTimeout ();
-      bool isLoopAlive ();
-      void initEventLoop ();
-      void runEventLoop ();
-      void pauseEventLoop ();
-      void stopEventLoop ();
-      void dispatchEventLoop (EventLoopDispatchCallback dispatch);
-      void signalDispatchEventLoop ();
-      void sleepEventLoop (int64_t ms);
-      void sleepEventLoop ();
-  };
-
-  String createJavaScript (const String& name, const String& source);
-
-  String getEmitToRenderProcessJavaScript (
-    const String& event,
-    const String& value,
-    const String& target,
-    const JSON::Object& options
-  );
-
-  String getEmitToRenderProcessJavaScript (
-    const String& event,
-    const String& value
-  );
-
-  String getResolveMenuSelectionJavaScript (
-    const String& seq,
-    const String& title,
-    const String& parent,
-    const String type = "system"
-  );
-
-  String getResolveToRenderProcessJavaScript (
-    const String& seq,
-    const String& state,
-    const String& value
-  );
-
-  void setcwd (const String& cwd);
-  const String getcwd ();
-  const String getcwd_state_value ();
-} // SSC
-
-#endif // SOCKET_RUNTIME_CORE_CORE_H
diff --git a/src/core/debug.hh b/src/core/debug.hh
deleted file mode 100644
index 4bcec8ba66..0000000000
--- a/src/core/debug.hh
+++ /dev/null
@@ -1,69 +0,0 @@
-#ifndef SOCKET_RUNTIME_CORE_DEBUG_H
-#define SOCKET_RUNTIME_CORE_DEBUG_H
-
-#include "config.hh"
-
-#if SOCKET_RUNTIME_PLATFORM_APPLE
-// Apple
-#ifndef debug
-// define `socket_runtime_os_log_debug` (macos/ios)
-#if defined(SSC_CLI)
-#  define socket_runtime_os_log_debug(...)
-#else
-static os_log_t SOCKET_RUNTIME_OS_LOG_DEBUG = nullptr;
-// wrap `os_log*` functions for global debugger
-#define socket_runtime_os_log_debug(format, fmt, ...) ({                       \
-  if (!SOCKET_RUNTIME_OS_LOG_DEBUG) {                                          \
-    static auto userConfig = SSC::getUserConfig();                             \
-    static auto bundleIdentifier = userConfig["meta_bundle_identifier"];       \
-    SOCKET_RUNTIME_OS_LOG_DEBUG = os_log_create(                               \
-      bundleIdentifier.c_str(),                                                \
-      "socket.runtime.debug"                                                   \
-    );                                                                         \
-  }                                                                            \
-                                                                               \
-  auto string = [NSString stringWithFormat: @fmt, ##__VA_ARGS__];              \
-  os_log_error(                                                                \
-    SOCKET_RUNTIME_OS_LOG_DEBUG,                                               \
-    "%{public}s",                                                              \
-    string.UTF8String                                                          \
-  );                                                                           \
-})
-#endif
-
-// define `debug(...)` macro
-#define debug(format, ...) ({                                                  \
-  NSLog(@format, ##__VA_ARGS__);                                               \
-  socket_runtime_os_log_debug("%{public}@", format, ##__VA_ARGS__);            \
-})
-#endif // `debug`
-#endif  // `__APPLE__`
-
-// Linux
-#if SOCKET_RUNTIME_PLATFORM_LINUX
-#  ifndef debug
-#    define debug(format, ...) fprintf(stderr, format "\n", ##__VA_ARGS__)
-#  endif // `debug`
-#endif // `__linux__`
-
-// Android (Linux)
-#if SOCKET_RUNTIME_PLATFORM_ANDROID
-#  ifndef debug
-#    define debug(format, ...)                                                 \
-      __android_log_print(                                                     \
-          ANDROID_LOG_DEBUG,                                                   \
-          "Console",                                                           \
-          format,                                                              \
-          ##__VA_ARGS__                                                        \
-        );
-#  endif // `debug`
-#endif // `__ANDROID__`
-
-// Windows
-#if SOCKET_RUNTIME_PLATFORM_WINDOWS && defined(DEBUG)
-#  define _WIN32_DEBUG 1
-#endif // `_WIN32 && DEBUG`
-#ifndef debug
-#  define debug(format, ...) fprintf(stderr, format "\n", ##__VA_ARGS__)
-#endif // `debug`
-#endif
diff --git a/src/core/file_system_watcher.hh b/src/core/file_system_watcher.hh
deleted file mode 100644
index fa73ebe7e8..0000000000
--- a/src/core/file_system_watcher.hh
+++ /dev/null
@@ -1,90 +0,0 @@
-#ifndef SOCKET_RUNTIME_FILE_SYSTEM_WATCHER_H
-#define SOCKET_RUNTIME_FILE_SYSTEM_WATCHER_H
-
-#include "../platform/platform.hh"
-
-namespace SSC {
-  class Core;
-  class FileSystemWatcher {
-    public:
-      // uv types
-      using EventHandle = uv_fs_event_t;
-      using PollHandle = uv_fs_poll_t;
-      using Loop = uv_loop_t;
-      using Async = uv_async_t;
-      using Stat = uv_stat_t;
-
-      struct Handle {
-        EventHandle event;
-        PollHandle poll;
-      };
-
-      // std types
-      using Clock = std::chrono::high_resolution_clock;
-      using HandleMap = std::map;
-      using Path = std::filesystem::path;
-      using Thread = std::thread;
-      using TimePoint = std::chrono::time_point;
-
-      // event types
-      enum class Event {
-        UNKNOWN,
-        RENAME,
-        CHANGE
-      };
-
-      // callback context
-      struct Context {
-        String name; // filename or directory name
-        FileSystemWatcher* watcher;
-        bool isDirectory = false;
-        TimePoint lastUpdated;
-      };
-
-      struct Options {
-        int debounce = 250; // in milliseconds
-        bool recursive = true;
-      };
-
-      using EventCallback = std::function&, // events
-        const Context& // event context
-      )>;
-
-      using ContextMap = std::map;
-
-      // state
-      EventCallback callback = nullptr;
-      Vector paths;
-      Vector watchedPaths;
-      ContextMap contexts;
-      HandleMap handles;
-      Options options;
-      AtomicBool ownsCore = false;
-      AtomicBool isRunning = false;
-      Core* core = nullptr;
-
-      static void handleEventCallback (
-        EventHandle* handle,
-        const char* filename,
-        int events,
-        int status
-      );
-
-      static void handlePollCallback (
-        PollHandle* handle,
-        int status,
-        const Stat* previousStat,
-        const Stat* currentStat
-      );
-
-      FileSystemWatcher (const String& path);
-      FileSystemWatcher (const Vector& paths);
-      ~FileSystemWatcher ();
-      bool start (EventCallback callback);
-      bool stop ();
-  };
-}
-
-#endif
diff --git a/src/core/headers.hh b/src/core/headers.hh
deleted file mode 100644
index a93e532743..0000000000
--- a/src/core/headers.hh
+++ /dev/null
@@ -1,88 +0,0 @@
-#ifndef SOCKET_RUNTIME_CORE_HEADERS_H
-#define SOCKET_RUNTIME_CORE_HEADERS_H
-
-#include "json.hh"
-
-namespace SSC {
-  class Headers {
-    public:
-      class Value {
-        public:
-          String string;
-          Value () = default;
-          Value (const String& value);
-          Value (const char* value);
-          Value (const Value& value);
-          Value (bool value);
-          Value (int value);
-          Value (float value);
-          Value (int64_t value);
-          Value (uint64_t value);
-          Value (double_t value);
-        #if SOCKET_RUNTIME_PLATFORM_APPLE
-          Value (ssize_t value);
-        #endif
-          bool operator == (const Value&) const;
-          bool operator != (const Value&) const;
-          bool operator == (const String&) const;
-          bool operator != (const String&) const;
-          const String operator + (const String&) const;
-
-          const String& str () const;
-          const char * c_str() const;
-          bool empty () const;
-          size_t size () const;
-
-          template  void set (T value) {
-            auto v = Value(value);
-            this->string = v.string;
-          }
-      };
-
-      class Header {
-        public:
-          String name;
-          Value value;
-          Header () = default;
-          Header (const Header& header);
-          Header (const String& name, const Value& value);
-          bool operator == (const Header&) const;
-          bool operator != (const Header&) const;
-          bool operator == (const String&) const;
-          bool operator != (const String&) const;
-          const String operator + (const String&) const;
-          size_t size () const;
-          bool empty () const;
-      };
-
-      using Entries = Vector
; - using Iterator = Entries::const_iterator; - - Entries entries; - Headers () = default; - Headers (const Headers& headers); - Headers (const String& source); - Headers (const Vector>& entries); - Headers (const Entries& entries); - size_t size () const; - String str () const; - bool empty () const; - - void set (const String& name, const String& value) noexcept; - void set (const Header& header) noexcept; - bool has (const String& name) const noexcept; - const Header get (const String& name) const noexcept; - Header& at (const String& name); - const Iterator begin () const noexcept; - const Iterator end () const noexcept; - bool erase (const String& name) noexcept; - const bool clear () noexcept; - String& operator [] (const String& name); - const String operator [] (const String& name) const noexcept; - JSON::Object json () const noexcept; - }; - - const String toHeaderCase (const String& source); -} - -#endif diff --git a/src/core/ini.hh b/src/core/ini.hh deleted file mode 100644 index 63ceebd8a4..0000000000 --- a/src/core/ini.hh +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef SOCKET_RUNTIME_CORE_INI_H -#define SOCKET_RUNTIME_CORE_INI_H - -#include "../platform/types.hh" - -namespace SSC::INI { - Map parse (const String& source); - Map parse (const String& source, const String& keyPathSeparator); -} -#endif diff --git a/src/core/io.hh b/src/core/io.hh deleted file mode 100644 index bbfceabe5c..0000000000 --- a/src/core/io.hh +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef SOCKET_RUNTIME_CORE_IO_H -#define SOCKET_RUNTIME_CORE_IO_H - -#include "../platform/types.hh" - -namespace SSC::IO { - void write (const String& input, bool isErrorOutput = false); -} - -#endif diff --git a/src/core/json.cc b/src/core/json.cc deleted file mode 100644 index 613ca1acf4..0000000000 --- a/src/core/json.cc +++ /dev/null @@ -1,33 +0,0 @@ -#include "json.hh" - -namespace SSC::JSON { - Null null; - - Null::Null (std::nullptr_t) - : Null() - {} - - std::nullptr_t Null::value () const { - return nullptr; - } - - const SSC::String Null::str () const { - return "null"; - } - - Raw::Raw (const Raw& raw) { - this->data = raw.data; - } - - Raw::Raw (const Raw* raw) { - this->data = raw->data; - } - - Raw::Raw (const SSC::String& source) { - this->data = source; - } - - const SSC::String Raw::str () const { - return this->data; - } -} diff --git a/src/core/json.hh b/src/core/json.hh deleted file mode 100644 index 7445f62c6e..0000000000 --- a/src/core/json.hh +++ /dev/null @@ -1,304 +0,0 @@ -#ifndef SOCKET_RUNTIME_CORE_JSON_H -#define SOCKET_RUNTIME_CORE_JSON_H - -#include "../platform/platform.hh" - -namespace SSC::JSON { - // forward - class Any; - class Raw; - class Null; - class Object; - class Array; - class Boolean; - class Number; - class String; - - using ObjectEntries = std::map; - using ArrayEntries = Vector; - - enum class Type { - Empty = -1, - Any = 0, - Null = 1, - Object = 2, - Array = 3, - Boolean = 4, - Number = 5, - String = 6, - Raw = 7, - Error = 8 - }; - - template struct Value { - public: - Type type = t; - D data; - - const SSC::String typeof () const { - switch (this->type) { - case Type::Empty: return SSC::String("empty"); - case Type::Raw: return SSC::String("raw"); - case Type::Any: return SSC::String("any"); - case Type::Array: return SSC::String("array"); - case Type::Boolean: return SSC::String("boolean"); - case Type::Number: return SSC::String("number"); - case Type::Null: return SSC::String("null"); - case Type::Object: return SSC::String("object"); - case Type::String: return SSC::String("string"); - case Type::Error: return SSC::String("error"); - } - } - - bool isError () const { return this->type == Type::Error; } - bool isRaw () const { return this->type == Type::Raw; } - bool isArray () const { return this->type == Type::Array; } - bool isBoolean () const { return this->type == Type::Boolean; } - bool isNumber () const { return this->type == Type::Number; } - bool isNull () const { return this->type == Type::Null; } - bool isObject () const { return this->type == Type::Object; } - bool isString () const { return this->type == Type::String; } - bool isEmpty () const { return this->type == Type::Empty; } - - const SSC::String str () const { - return ""; - } - }; - - class Error : public std::invalid_argument, public Value { - public: - int code = 0; - SSC::String name; - SSC::String message; - SSC::String location; - - Error (); - Error (const SSC::String& message); - Error (const Error& error); - Error (Error* error); - Error ( - const SSC::String& name, - const SSC::String& message, - int code = 0 - ); - Error ( - const SSC::String& name, - const SSC::String& message, - const SSC::String& location - ); - - const SSC::String value () const; - const char* what () const noexcept override; - const SSC::String str () const; - }; - - class Null : public Value { - public: - Null () = default; - Null (std::nullptr_t); - std::nullptr_t value () const; - const SSC::String str () const; - }; - - class Any : public Value { - public: - SharedPointer pointer = nullptr; - - Any (); - Any (const Any& any); - Any (Type type, SharedPointer pointer); - Any (std::nullptr_t); - Any (const Null); - - Any (bool); - Any (int64_t); - Any (uint64_t); - Any (uint32_t); - Any (int32_t); - Any (double); - #if SOCKET_RUNTIME_PLATFORM_APPLE - Any (size_t); - Any (ssize_t); - #elif !SOCKET_RUNTIME_PLATFORM_WINDOWS - Any (long long); - #endif - - Any (Atomic&); - Any (Atomic&); - Any (Atomic&); - Any (Atomic&); - Any (Atomic&); - Any (Atomic&); - #if SOCKET_RUNTIME_PLATFORM_APPLE - Any (Atomic&); - Any (Atomic&); - #elif !SOCKET_RUNTIME_PLATFORM_WINDOWS - Any (Atomic&); - #endif - - Any (const char); - Any (const char *); - - Any (const SSC::String&); - Any (const SSC::Path&); - - Any (const Boolean&); - Any (const Number&); - Any (const String&); - Any (const Object&); - Any (const Array&); - Any (const Raw&); - Any (const Error&); - #if SOCKET_RUNTIME_PLATFORM_APPLE - Any (const NSError*); - #elif SOCKET_RUNTIME_PLATFORM_LINUX - Any (const GError*); - #endif - - Any (const ArrayEntries&); - Any (const ObjectEntries&); - - ~Any (); - - Any operator[](const SSC::String& key) const; - Any& operator[](const SSC::String& key); - Any operator[](const unsigned int index) const; - Any& operator[](const unsigned int index); - - bool operator == (const Any&) const; - bool operator != (const Any&) const; - - template T& as () const { - auto ptr = this->pointer.get(); - - if (ptr != nullptr && this->type != Type::Null) { - return *reinterpret_cast(ptr); - } - - throw Error("BadCastError", "cannot cast to null value", __PRETTY_FUNCTION__); - } - - const SSC::String str () const; - }; - - class Raw : public Value { - public: - Raw (const Raw& raw); - Raw (const Raw* raw); - Raw (const SSC::String& source); - const SSC::String str () const; - }; - - class Object : public Value { - public: - using Entries = ObjectEntries; - using const_iterator = Entries::const_iterator; - Object () = default; - #if SOCKET_RUNTIME_PLATFORM_LINUX - Object (JSCValue* value); - #endif - Object (const std::map& entries); - Object (const std::map& entries); - Object (const std::map& entries); - Object (const std::map& entries); - Object (const Object::Entries& entries); - Object (const Object& object); - Object (const std::map& map); - Object (const Error& error); - - Any operator [] (const SSC::String& key) const; - Any &operator [] (const SSC::String& key); - - const SSC::String str () const; - const Object::Entries value () const; - const Any& get (const SSC::String& key) const; - Any& get (const SSC::String& key); - void set (const SSC::String& key, const Any& value); - bool has (const SSC::String& key) const; - bool contains (const SSC::String& key) const; - Entries::size_type size () const; - const const_iterator begin () const noexcept; - const const_iterator end () const noexcept; - }; - - class Array : public Value { - public: - using Entries = ArrayEntries; - using const_iterator = Entries::const_iterator; - Array () = default; - Array (const Array& array); - Array (const Array::Entries& entries); - #if SOCKET_RUNTIME_PLATFORM_LINUX - Array (JSCValue* value); - #endif - - Any operator [] (const unsigned int index) const; - Any &operator [] (const unsigned int index); - - const SSC::String str () const; - Array::Entries value () const; - bool has (const unsigned int index) const; - Entries::size_type size () const; - Any get (const unsigned int index) const; - void set (const unsigned int index, const Any& value); - void push (Any value); - Any& pop (); - const const_iterator begin () const noexcept; - const const_iterator end () const noexcept; - }; - - class Boolean : public Value { - public: - Boolean () = default; - Boolean (const Boolean& boolean); - Boolean (bool boolean); - Boolean (int data); - Boolean (int64_t data); - Boolean (double data); - Boolean (void *data); - Boolean (const SSC::String& string); - - bool value () const; - const SSC::String str () const; - }; - - class Number : public Value { - public: - Number () = default; - Number (const Number& number); - Number (double number); - Number (char number); - Number (int number); - Number (int64_t number); - Number (bool number); - Number (const String& string); - - float value () const; - const SSC::String str () const; - }; - - class String : public Value { - public: - String () = default; - String (const String& data); - String (const SSC::String& data); - String (const char data); - String (const char *data); - String (const Any& any); - String (const Number& number); - String (const Boolean& boolean); - String (const Error& error); - - const SSC::String str () const; - SSC::String value () const; - SSC::String::size_type size () const; - }; - - extern Null null; - extern Any anyNull; - - inline const auto typeof (const Any& any) { - return any.typeof(); - } -} -#endif diff --git a/src/core/json/any.cc b/src/core/json/any.cc deleted file mode 100644 index 2737737e17..0000000000 --- a/src/core/json/any.cc +++ /dev/null @@ -1,268 +0,0 @@ -#include "../json.hh" - -namespace SSC::JSON { - Any anyNull = nullptr; - - Any::Any () { - this->pointer = nullptr; - this->type = Type::Null; - } - - Any::Any (const Any& any) : pointer(any.pointer) { - this->type = any.type; - } - - Any::Any (Type type, SharedPointer pointer) : pointer(pointer) { - this->type = type; - } - - Any::Any (const Null null) { - this->pointer = SharedPointer(new Null()); - this->type = Type::Null; - } - - Any::Any (std::nullptr_t) { - this->pointer = SharedPointer(new Null()); - this->type = Type::Null; - } - - Any::Any (const char *string) { - this->pointer = SharedPointer(new String(string)); - this->type = Type::String; - } - - Any::Any (const char string) { - this->pointer = SharedPointer(new String(string)); - this->type = Type::String; - } - - Any::Any (const SSC::Path& path) - : Any(path.string()) - {} - - Any::Any (const SSC::String& string) { - this->pointer = SharedPointer(new String(string)); - this->type = Type::String; - } - - Any::Any (const String& string) { - this->pointer = SharedPointer(new String(string)); - this->type = Type::String; - } - - Any::Any (bool boolean) { - this->pointer = SharedPointer(new Boolean(boolean)); - this->type = Type::Boolean; - } - - Any::Any (const Boolean& boolean) { - this->pointer = SharedPointer(new Boolean(boolean)); - this->type = Type::Boolean; - } - - Any::Any (int32_t number) { - this->pointer = SharedPointer(new Number((double) number)); - this->type = Type::Number; - } - - Any::Any (uint32_t number) { - this->pointer = SharedPointer(new Number((double) number)); - this->type = Type::Number; - } - - Any::Any (int64_t number) { - this->pointer = SharedPointer(new Number((double) number)); - this->type = Type::Number; - } - - Any::Any (uint64_t number) { - this->pointer = SharedPointer(new Number((double) number)); - this->type = Type::Number; - } - - Any::Any (double number) { - this->pointer = SharedPointer(new Number((double) number)); - this->type = Type::Number; - } - -#if SOCKET_RUNTIME_PLATFORM_APPLE - Any::Any (size_t number) { - this->pointer = SharedPointer(new Number((double) number)); - this->type = Type::Number; - } - - Any::Any (ssize_t number) { - this->pointer = SharedPointer(new Number((double) number)); - this->type = Type::Number; - } -#elif !SOCKET_RUNTIME_PLATFORM_WINDOWS - Any::Any (long long number) { - this->pointer = SharedPointer(new Number((double) number)); - this->type = Type::Number; - } -#endif - - Any::Any (Atomic& boolean) : Any(boolean.load()) {} - Any::Any (Atomic& number) : Any(number.load()) {} - Any::Any (Atomic& number) : Any(number.load()) {} - Any::Any (Atomic& number) : Any(number.load()) {} - Any::Any (Atomic& number) : Any(number.load()) {} - Any::Any (Atomic& number) : Any(number.load()) {} - - Any::Any (const Number& number) { - this->pointer = SharedPointer(new Number(number)); - this->type = Type::Number; - } - - Any::Any (const Object& object) { - this->pointer = SharedPointer(new Object(object)); - this->type = Type::Object; - } - - Any::Any (const Object::Entries& entries) { - this->pointer = SharedPointer(new Object(entries)); - this->type = Type::Object; - } - - Any::Any (const Array& array) { - this->pointer = SharedPointer(new Array(array)); - this->type = Type::Array; - } - - Any::Any (const Array::Entries& entries) { - this->pointer = SharedPointer(new Array(entries)); - this->type = Type::Array; - } - - Any::Any (const Raw& source) { - this->pointer = SharedPointer(new Raw(source)); - this->type = Type::Raw; - } - - Any::Any (const Error& error) { - this->pointer = SharedPointer(new Error(error)); - this->type = Type::Error; - } - -#if SOCKET_RUNTIME_PLATFORM_APPLE - Any::Any (const NSError* error) { - this->type = Type::Error; - this->pointer = SharedPointer(new Error( - error.domain.UTF8String, - error.localizedDescription.UTF8String, - error.code - )); - } -#elif SOCKET_RUNTIME_PLATFORM_LINUX - Any::Any (const GError* error) { - this->type = Type::Error; - this->pointer = SharedPointer(new Error( - g_quark_to_string(error->domain), - error->message, - error->code - )); - } -#endif - - Any::~Any () { - this->pointer = nullptr; - this->type = Type::Any; - } - - Any Any::operator[](const SSC::String& key) const { - if (this->type == Type::Object) { - return this->as()[key]; - } - throw Error("TypeError", "cannot use operator[] on non-object type", __PRETTY_FUNCTION__); - } - - Any& Any::operator[](const SSC::String& key) { - if (this->type == Type::Object) { - return this->as()[key]; - } - throw Error("TypeError", "cannot use operator[] on non-object type", __PRETTY_FUNCTION__); - } - - Any Any::operator[](const unsigned int index) const { - if (this->type == Type::Array) { - return this->as()[index]; - } - throw Error("TypeError", "cannot use operator[] on non-array type", __PRETTY_FUNCTION__); - } - - Any& Any::operator[](const unsigned int index) { - if (this->type == Type::Array) { - return this->as()[index]; - } - throw Error("TypeError", "cannot use operator[] on non-array type", __PRETTY_FUNCTION__); - } - - bool Any::operator == (const Any& input) const { - if (this->isEmpty() && input.isEmpty()) { - return true; - } - - if (this->isNull() && input.isNull()) { - return true; - } - - if (this->isString() && input.isString()) { - return this->str() == input.str(); - } - - if (this->isNumber() && input.isNumber()) { - return this->as().value() == input.as().value(); - } - - if (this->isBoolean() && input.isBoolean()) { - return this->as().value() == input.as().value(); - } - - return false; - } - - bool Any::operator != (const Any& input) const { - if (this->isEmpty() && !input.isEmpty()) { - return true; - } - - if (this->isNull() && !input.isNull()) { - return true; - } - - if (this->isString() && input.isString()) { - return this->str() != input.str(); - } - - if (this->isNumber() && input.isNumber()) { - return this->as().value() != input.as().value(); - } - - if (this->isBoolean() && input.isBoolean()) { - return this->as().value() != input.as().value(); - } - - return true; - } - - const SSC::String Any::str () const { - const auto ptr = this->pointer.get() == nullptr - ? reinterpret_cast(this) - : this->pointer.get(); - - switch (this->type) { - case Type::Empty: return ""; - case Type::Any: return ""; - case Type::Raw: return reinterpret_cast(ptr)->str(); - case Type::Null: return "null"; - case Type::Object: return reinterpret_cast(ptr)->str(); - case Type::Array: return reinterpret_cast(ptr)->str(); - case Type::Boolean: return reinterpret_cast(ptr)->str(); - case Type::Number: return reinterpret_cast(ptr)->str(); - case Type::String: return reinterpret_cast(ptr)->str(); - case Type::Error: return reinterpret_cast(ptr)->str(); - } - - return ""; - } -} diff --git a/src/core/json/object.cc b/src/core/json/object.cc deleted file mode 100644 index a45542d3bd..0000000000 --- a/src/core/json/object.cc +++ /dev/null @@ -1,179 +0,0 @@ -#include "../json.hh" - -namespace SSC::JSON { -#if SOCKET_RUNTIME_PLATFORM_LINUX - Object::Object (JSCValue* value) { - if (value != nullptr && jsc_value_is_object(value) && !jsc_value_is_array(value)) { - const auto keys = jsc_value_object_enumerate_properties(value); - if (keys != nullptr) { - for (int i = 0; keys[i] != nullptr; ++i) { - const auto key = keys[i]; - const auto property = jsc_value_object_get_property(value, key); - if (jsc_value_is_string(property)) { - this->set(key, JSON::String(jsc_value_to_string(property))); - } else if (jsc_value_is_boolean(property)) { - this->set(key, JSON::Boolean(jsc_value_to_boolean(property))); - } else if (jsc_value_is_null(property)) { - this->set(key, JSON::Null()); - } else if (jsc_value_is_number(property)) { - this->set(key, JSON::Number(jsc_value_to_double(property))); - } else if (jsc_value_is_array(property)) { - this->set(key, JSON::Array(property)); - } else if (jsc_value_is_object(property)) { - this->set(key, JSON::Object(property)); - } - } - - g_strfreev(keys); - } - } - } -#endif - Object::Object (const std::map& entries) { - for (auto const &tuple : entries) { - auto key = tuple.first; - auto value = tuple.second; - this->data.insert_or_assign(key, value); - } - } - - Object::Object (const std::map& entries) { - for (auto const &tuple : entries) { - auto key = tuple.first; - auto value = tuple.second; - this->data.insert_or_assign(key, value); - } - } - - Object::Object (const std::map& entries) { - for (auto const &tuple : entries) { - auto key = tuple.first; - auto value = tuple.second; - this->data.insert_or_assign(key, value); - } - } - - Object::Object (const std::map& entries) { - for (auto const &tuple : entries) { - auto key = tuple.first; - auto value = tuple.second; - this->data.insert_or_assign(key, value); - } - } - - Object::Object (const Object::Entries& entries) { - for (const auto& tuple : entries) { - auto key = tuple.first; - auto value = tuple.second; - this->data.insert_or_assign(key, value); - } - } - - Object::Object (const Object& object) { - this->data = object.value(); - } - - Object::Object (const std::map& map) { - for (const auto& tuple : map) { - auto key = tuple.first; - auto value = Any(tuple.second); - this->data.insert_or_assign(key, value); - } - } - - Object::Object (const Error& error) { - if (error.name.size() > 0) { - this->set("name", error.name); - } - - if (error.message.size() > 0) { - this->set("message", error.message); - } - - if (error.location.size() > 0) { - this->set("location", error.location); - } - - if (error.code != 0) { - this->set("code", error.code); - } - } - - const SSC::String Object::str () const { - SSC::StringStream stream; - auto count = this->data.size(); - stream << SSC::String("{"); - for (const auto& tuple : this->data) { - auto key = replace(tuple.first, "\"","\\\""); - auto value = tuple.second.str(); - - stream << SSC::String("\""); - stream << key; - stream << SSC::String("\":"); - stream << value; - - if (--count > 0) { - stream << SSC::String(","); - } - } - - stream << SSC::String("}"); - return stream.str(); - } - - const Object::Entries Object::value () const { - return this->data; - } - - const Any& Object::get (const SSC::String& key) const { - if (this->data.find(key) != this->data.end()) { - return this->data.at(key); - } - - return anyNull; - } - - Any& Object::get (const SSC::String& key) { - if (this->data.find(key) != this->data.end()) { - return this->data.at(key); - } - - return anyNull; - } - - void Object::set (const SSC::String& key, const Any& value) { - this->data[key] = value; - } - - bool Object::has (const SSC::String& key) const { - return this->contains(key); - } - - bool Object::contains (const SSC::String& key) const { - return this->data.contains(key); - } - - Any Object::operator [] (const SSC::String& key) const { - if (this->data.find(key) != this->data.end()) { - return this->data.at(key); - } - - return nullptr; - } - - Any& Object::operator [] (const SSC::String& key) { - return this->data[key]; - } - - ObjectEntries::size_type Object::size () const { - return this->data.size(); - } - - const Object::const_iterator Object::begin () const noexcept { - return this->data.begin(); - } - - const Object::const_iterator Object::end () const noexcept { - return this->data.end(); - } -} diff --git a/src/core/modules/ai.hh b/src/core/modules/ai.hh deleted file mode 100644 index 21561891ff..0000000000 --- a/src/core/modules/ai.hh +++ /dev/null @@ -1,129 +0,0 @@ -#ifndef SOCKET_RUNTIME_CORE_AI_H -#define SOCKET_RUNTIME_CORE_AI_H - -#include -#include - -#include "../module.hh" - -// #include -// #include -// #include -// #include - -namespace SSC { - class LLM; - class Core; - - struct LLMOptions { - bool conversation = false; - bool chatml = false; - bool instruct = false; - int n_ctx = 0; - int n_keep = 0; - int n_batch = 0; - int n_threads = 0; - int n_gpu_layers = 0; - int n_predict = 0; - int grp_attn_n = 0; - int grp_attn_w = 0; - int seed = 0; - int max_tokens = 0; - int top_k = 0; - float top_p = 0.0; - float min_p = 0.0; - float tfs_z = 0.0; - float typical_p = 0.0; - float temp; - - String path; - String prompt; - String antiprompt; - }; - - class CoreAI : public CoreModule { - public: - using ID = uint64_t; - using LLMs = std::map>; - - Mutex mutex; - LLMs llms; - - void chatLLM ( - const String& seq, - ID id, - String message, - const CoreModule::Callback& callback - ); - - void createLLM ( - const String& seq, - ID id, - LLMOptions options, - const CoreModule::Callback& callback - ); - - void destroyLLM ( - const String& seq, - ID id, - const CoreModule::Callback& callback - ); - - void stopLLM ( - const String& seq, - ID id, - const CoreModule::Callback& callback - ); - - bool hasLLM (ID id); - SharedPointer getLLM (ID id); - - CoreAI (Core* core) - : CoreModule(core) - {} - }; - - class LLM { - using Cb = std::function; - using Logger = std::function; - - gpt_params params; - llama_model* model = nullptr; - llama_context* ctx = nullptr; - llama_context* guidance = nullptr; - struct llama_sampling_context* sampling; - - std::vector* input_tokens = nullptr; - std::ostringstream* output_ss = nullptr; - std::vector* output_tokens = nullptr; - std::vector session_tokens; - std::vector embd_inp; - std::vector guidance_inp; - std::vector> antiprompt_ids; - - String path_session = ""; - int guidance_offset = 0; - int original_prompt_len = 0; - int n_ctx = 0; - int n_past = 0; - int n_consumed = 0; - int n_session_consumed = 0; - int n_past_guidance = 0; - - public: - String err = ""; - bool stopped = false; - bool interactive = false; - - void chat (String input, const Cb cb); - void escape (String& input); - - LLM(LLMOptions options); - ~LLM(); - - static void tramp(ggml_log_level level, const char* message, void* user_data); - static Logger log; - }; -} - -#endif diff --git a/src/core/modules/child_process.hh b/src/core/modules/child_process.hh deleted file mode 100644 index 498547397e..0000000000 --- a/src/core/modules/child_process.hh +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef SOCKET_RUNTIME_CORE_MODULE_CHILD_PROCESS_H -#define SOCKET_RUNTIME_CORE_MODULE_CHILD_PROCESS_H - -#include "../module.hh" -#include "../process.hh" - -namespace SSC { - class Core; - class CoreChildProcess : public CoreModule { - public: - using ID = uint64_t; - using Handles = std::map>; - - struct SpawnOptions { - String cwd; - const Vector env; - bool allowStdin = true; - bool allowStdout = true; - bool allowStderr = true; - }; - - struct ExecOptions { - String cwd; - const Vector env; - bool allowStdout = true; - bool allowStderr = true; - uint64_t timeout = 0; - #if SOCKET_RUNTIME_PLATFORM_WINDOWS || SOCKET_RUNTIME_PLATFORM_IOS - int killSignal = 0; // unused - #else - int killSignal = SIGTERM; - #endif - }; - - Handles handles; - Mutex mutex; - - CoreChildProcess (Core* core) - : CoreModule(core) - {} - - void shutdown (); - void exec ( - const String& seq, - ID id, - const Vector args, - const ExecOptions options, - const CoreModule::Callback& callback - ); - - void spawn ( - const String& seq, - ID id, - const Vector args, - const SpawnOptions options, - const CoreModule::Callback& callback - ); - - void kill ( - const String& seq, - ID id, - int signal, - const CoreModule::Callback& callback - ); - - void write ( - const String& seq, - ID id, - SharedPointer buffer, - size_t size, - const CoreModule::Callback& callback - ); - }; -} -#endif diff --git a/src/core/modules/conduit.hh b/src/core/modules/conduit.hh deleted file mode 100644 index 4466a0e135..0000000000 --- a/src/core/modules/conduit.hh +++ /dev/null @@ -1,181 +0,0 @@ -#ifndef SOCKET_RUNTIME_CORE_CONDUIT_H -#define SOCKET_RUNTIME_CORE_CONDUIT_H - -#include "../module.hh" -#include "timers.hh" -#include - -namespace SSC { - class Core; - - class CoreConduit : public CoreModule { - public: - using Options = std::unordered_map; - using StartCallback = Function; - - struct DecodedMessage { - Options options; - Vector payload; - - inline String get (const String& key) const { - const auto it = options.find(key); - if (it != options.end()) { - return it->second; - } - return ""; - } - - inline bool has (const String& key) const { - const auto it = options.find(key); - if (it != options.end()) { - return true; - } - return false; - } - - inline String pluck (const String& key) { - auto it = options.find(key); - if (it != options.end()) { - String value = it->second; - options.erase(it); - return value; - } - return ""; - } - - inline Map getOptionsAsMap () { - Map map; - - for (const auto& pair : this->options) { - map.insert(pair); - } - return map; - } - }; - - struct FrameBuffer { - Vector vector; - - inline const size_t size () const { - return this->vector.size(); - } - - inline const unsigned char* data () const { - return this->vector.data(); - } - - inline void resize (const size_t size) { - this->vector.resize(size); - } - - unsigned char operator [] (const unsigned int index) const { - if (index >= this->size()) { - return 0; - } - - return this->vector.at(index); - } - - unsigned char& operator [] (const unsigned int index) { - if (index >= this->size()) { - this->vector.resize(index + 1); - } - - return this->vector.at(index); - } - - const Vector slice ( - Vector::const_iterator& begin, - Vector::const_iterator& end - ) { - return std::move(Vector(begin, end)); - } - - template - const Vector slice ( - Vector::size_type start, - Vector::size_type end - ) { - return Vector(this->vector.begin() + start, this->vector.begin() + end); - } - }; - - class Client { - public: - using CloseCallback = Function; - using ID = uint64_t; - - // client state - ID id = 0; - ID clientId = 0; - Atomic isHandshakeDone = false; - Atomic isClosing = false; - Atomic isClosed = false; - - // uv state - uv_tcp_t handle; - uv_buf_t buffer; - uv_stream_t* stream = nullptr; - - // websocket frame buffer state - FrameBuffer frameBuffer; - uint64_t frameBufferOffset = 0; - Vector queue; - CoreConduit* conduit = nullptr; - - Client (CoreConduit* conduit) - : conduit(conduit), - id(0), - clientId(0), - isHandshakeDone(0) - {} - - ~Client (); - - bool send ( - const CoreConduit::Options& options, - SharedPointer payload, - size_t length, - int opcode = 2, - const Function callback = nullptr - ); - - void close (const CloseCallback& callback = nullptr); - }; - - // state - std::map clients; - String sharedKey; - Atomic isStarting = false; - Atomic port = 0; - String hostname = "0.0.0.0"; - Mutex mutex; - - CoreConduit (Core* core); - ~CoreConduit (); - - // codec - DecodedMessage decodeMessage (const Vector& data); - Vector encodeMessage ( - const Options& options, - const Vector& payload - ); - - // client access - bool has (uint64_t id); - CoreConduit::Client* get (uint64_t id); - - // lifecycle - void start (const StartCallback& callback = nullptr); - void stop (); - bool isActive (); - - private: - uv_tcp_t socket; - struct sockaddr_in addr; - - void handshake (Client* client, const char *request); - void processFrame (Client* client, const char *frame, ssize_t size); - }; -} -#endif diff --git a/src/core/modules/dns.hh b/src/core/modules/dns.hh deleted file mode 100644 index 01e4fc9ae1..0000000000 --- a/src/core/modules/dns.hh +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef SOCKET_RUNTIME_CORE_MODULE_DNS_H -#define SOCKET_RUNTIME_CORE_MODULE_DNS_H - -#include "../module.hh" - -namespace SSC { - class Core; - class CoreDNS : public CoreModule { - public: - struct LookupOptions { - String hostname; - int family; - // TODO: support these options - // - hints - // - all - // -verbatim - }; - - CoreDNS (Core* core) - : CoreModule(core) - {} - - void lookup ( - const String& seq, - const LookupOptions& options, - const CoreModule::Callback& callback - ) const; - }; -} -#endif diff --git a/src/core/modules/fs.hh b/src/core/modules/fs.hh deleted file mode 100644 index fd0c5afb5a..0000000000 --- a/src/core/modules/fs.hh +++ /dev/null @@ -1,329 +0,0 @@ -#ifndef SOCKET_RUNTIME_CORE_MODULE_FS_H -#define SOCKET_RUNTIME_CORE_MODULE_FS_H - -#include "../file_system_watcher.hh" -#include "../module.hh" -#include "../resource.hh" -#include "../trace.hh" - -namespace SSC { - class Core; - class CoreFS : public CoreModule { - public: - using ID = uint64_t; - - struct Descriptor { - ID id; - Atomic retained = false; - Atomic stale = false; - FileResource resource; - Mutex mutex; - uv_dir_t *dir = nullptr; - uv_file fd = 0; - CoreFS* fs = nullptr; - - #if SOCKET_RUNTIME_PLATFORM_ANDROID - // asset state - Android::Asset* androidAsset = nullptr; - Queue androidAssetDirectoryEntries; - Queue androidContentDirectoryEntries; - Android::ContentResolver::FileDescriptor androidContent = nullptr; - // type predicates - bool isAndroidAssetDirectory = false; - bool isAndroidContentDirectory = false; - bool isAndroidContent = false; - // descriptor offsets - off_t androidAssetOffset = 0; - off_t androidAssetLength = 0; - off_t androidContentOffset = 0; - off_t androidContentLength = 0; - #endif - - Descriptor (CoreFS* fs, ID id, const String& filename); - bool isDirectory () const; - bool isFile () const; - bool isRetained () const; - bool isStale () const; - }; - - struct RequestContext : CoreModule::RequestContext { - ID id; - SharedPointer descriptor = nullptr; - SharedPointer buffer = nullptr; - Tracer tracer; - uv_fs_t req; - uv_buf_t buf; - // 256 which corresponds to DirectoryHandle.MAX_BUFFER_SIZE - uv_dirent_t dirents[256]; - int offset = 0; - int result = 0; - bool recursive; - - RequestContext () = delete; - RequestContext (SharedPointer descriptor) - : RequestContext(descriptor, "", nullptr) - {} - - RequestContext (const String& seq, const Callback& callback) - : RequestContext(nullptr, seq, callback) - {} - - RequestContext ( - SharedPointer descriptor, - const String& seq, - const Callback& callback - ) : tracer("CoreFS::RequestContext") { - this->id = rand64(); - this->seq = seq; - this->req.data = (void*) this; - this->callback = callback; - this->descriptor = descriptor; - this->recursive = false; - this->req.loop = nullptr; - } - - ~RequestContext () { - if (this->req.loop) { - uv_fs_req_cleanup(&this->req); - } - } - - void setBuffer (SharedPointer base, uint32_t size); - }; - - std::map> watchers; - std::map> descriptors; - Mutex mutex; - - CoreFS (Core* core) - : CoreModule(core) - {} - - SharedPointer getDescriptor (ID id) const; - SharedPointer getDescriptor (ID id); - void removeDescriptor (ID id); - bool hasDescriptor (ID id) const; - - void constants ( - const String& seq, - const CoreModule::Callback& callback - ) const; - - void access ( - const String& seq, - const String& path, - int mode, - const CoreModule::Callback& callback - ); - - void chmod ( - const String& seq, - const String& path, - int mode, - const CoreModule::Callback& callback - ) const; - - void chown ( - const String& seq, - const String& path, - uv_uid_t uid, - uv_gid_t gid, - const CoreModule::Callback& callback - ) const; - - void lchown ( - const String& seq, - const String& path, - uv_uid_t uid, - uv_gid_t gid, - const CoreModule::Callback& callback - ) const; - - void close ( - const String& seq, - ID id, - const CoreModule::Callback& callback - ); - - void copyFile ( - const String& seq, - const String& src, - const String& dst, - int flags, - const CoreModule::Callback& callback - ); - - void closedir ( - const String& seq, - ID id, - const CoreModule::Callback& callback - ); - - void closeOpenDescriptor ( - const String& seq, - ID id, - const CoreModule::Callback& callback - ); - - void closeOpenDescriptors ( - const String& seq, - const CoreModule::Callback& callback - ); - - void closeOpenDescriptors ( - const String& seq, - bool preserveRetained, - const CoreModule::Callback& callback - ); - - void fstat ( - const String& seq, - ID id, - const CoreModule::Callback& callback - ); - - void fsync ( - const String& seq, - ID id, - const CoreModule::Callback& callback - ) const; - - void ftruncate ( - const String& seq, - ID id, - int64_t offset, - const CoreModule::Callback& callback - ) const; - - void getOpenDescriptors ( - const String& seq, - const CoreModule::Callback& callback - ) const; - - void lstat ( - const String& seq, - const String& path, - const CoreModule::Callback& callback - ); - - void link ( - const String& seq, - const String& src, - const String& dest, - const CoreModule::Callback& callback - ) const; - - void symlink ( - const String& seq, - const String& src, - const String& dest, - int flags, - const CoreModule::Callback& callback - ) const; - - void mkdir ( - const String& seq, - const String& path, - int mode, - bool recursive, - const CoreModule::Callback& callback - ) const; - - void readlink ( - const String& seq, - const String& path, - const CoreModule::Callback& callback - ) const; - - void realpath ( - const String& seq, - const String& path, - const CoreModule::Callback& callback - ); - - void open ( - const String& seq, - ID id, - const String& path, - int flags, - int mode, - const CoreModule::Callback& callback - ); - - void opendir ( - const String& seq, - ID id, - const String& path, - const CoreModule::Callback& callback - ); - - void read ( - const String& seq, - ID id, - size_t len, - size_t offset, - const CoreModule::Callback& callback - ) const; - - void readdir ( - const String& seq, - ID id, - size_t entries, - const CoreModule::Callback& callback - ) const; - - void retainOpenDescriptor ( - const String& seq, - ID id, - const CoreModule::Callback& callback - ); - - void rename ( - const String& seq, - const String& src, - const String& dst, - const CoreModule::Callback& callback - ) const; - - void rmdir ( - const String& seq, - const String& path, - const CoreModule::Callback& callback - ) const; - - void stat ( - const String& seq, - const String& path, - const CoreModule::Callback& callback - ); - - void stopWatch ( - const String& seq, - ID id, - const CoreModule::Callback& callback - ); - - void unlink ( - const String& seq, - const String& path, - const CoreModule::Callback& callback - ) const; - - void watch ( - const String& seq, - ID id, - const String& path, - const CoreModule::Callback& callback - ); - - void write ( - const String& seq, - ID id, - SharedPointer bytes, - size_t size, - size_t offset, - const CoreModule::Callback& callback - ) const; - }; -} -#endif diff --git a/src/core/modules/media_devices.cc b/src/core/modules/media_devices.cc deleted file mode 100644 index 4344cfc9d1..0000000000 --- a/src/core/modules/media_devices.cc +++ /dev/null @@ -1,31 +0,0 @@ -#include "media_devices.hh" -#include "../debug.hh" - -namespace SSC { - CoreMediaDevices::CoreMediaDevices (Core* core) - : CoreModule(core), - permissionChangeObservers() - {} - - CoreMediaDevices::~CoreMediaDevices () {} - - template<> bool CoreModule::template Observers>::add( - const CoreModule::Observer&, - CoreModule::Observer::Callback - ); - - template<> bool CoreMediaDevices::PermissionChangeObservers::remove( - const CoreMediaDevices::PermissionChangeObserver& - ); - - bool CoreMediaDevices::addPermissionChangeObserver ( - const PermissionChangeObserver& observer, - const PermissionChangeObserver::Callback callback - ) { - return this->permissionChangeObservers.add(observer, callback); - } - - bool CoreMediaDevices::removePermissionChangeObserver (const PermissionChangeObserver& observer) { - return this->permissionChangeObservers.remove(observer); - } -} diff --git a/src/core/modules/media_devices.hh b/src/core/modules/media_devices.hh deleted file mode 100644 index 119bc26c2d..0000000000 --- a/src/core/modules/media_devices.hh +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef SOCKET_RUNTIME_CORE_MODULE_MEDIA_DEVICES_H -#define SOCKET_RUNTIME_CORE_MODULE_MEDIA_DEVICES_H - -#include "../module.hh" - -namespace SSC { - class CoreMediaDevices : public CoreModule { - public: - using PermissionChangeObserver = CoreModule::Observer; - using PermissionChangeObservers = CoreModule::Observers; - - PermissionChangeObservers permissionChangeObservers; - - CoreMediaDevices (Core* core); - ~CoreMediaDevices (); - - bool removePermissionChangeObserver ( - const PermissionChangeObserver& observer - ); - - bool addPermissionChangeObserver ( - const PermissionChangeObserver& observer, - const PermissionChangeObserver::Callback callback - ); - }; -} -#endif diff --git a/src/core/modules/os.cc b/src/core/modules/os.cc deleted file mode 100644 index b1c3d90f22..0000000000 --- a/src/core/modules/os.cc +++ /dev/null @@ -1,670 +0,0 @@ -#include "../core.hh" -#include "../json.hh" -#include "udp.hh" -#include "os.hh" - -namespace SSC { - #define CONSTANT(c) { #c, (c) }, - static const std::map OS_CONSTANTS = { - #if defined(E2BIG) - CONSTANT(E2BIG) - #endif - #if defined(EACCES) - CONSTANT(EACCES) - #endif - #if defined(EADDRINUSE) - CONSTANT(EADDRINUSE) - #endif - #if defined(EADDRNOTAVAIL) - CONSTANT(EADDRNOTAVAIL) - #endif - #if defined(EAFNOSUPPORT) - CONSTANT(EAFNOSUPPORT) - #endif - #if defined(EAGAIN) - CONSTANT(EAGAIN) - #endif - #if defined(EALREADY) - CONSTANT(EALREADY) - #endif - #if defined(EBADF) - CONSTANT(EBADF) - #endif - #if defined(EBADMSG) - CONSTANT(EBADMSG) - #endif - #if defined(EBUSY) - CONSTANT(EBUSY) - #endif - #if defined(ECANCELED) - CONSTANT(ECANCELED) - #endif - #if defined(ECHILD) - CONSTANT(ECHILD) - #endif - #if defined(ECONNABORTED) - CONSTANT(ECONNABORTED) - #endif - #if defined(ECONNREFUSED) - CONSTANT(ECONNREFUSED) - #endif - #if defined(ECONNRESET) - CONSTANT(ECONNRESET) - #endif - #if defined(EDEADLK) - CONSTANT(EDEADLK) - #endif - #if defined(EDESTADDRREQ) - CONSTANT(EDESTADDRREQ) - #endif - #if defined(EDOM) - CONSTANT(EDOM) - #endif - #if defined(EDQUOT) - CONSTANT(EDQUOT) - #endif - #if defined(EEXIST) - CONSTANT(EEXIST) - #endif - #if defined(EFAULT) - CONSTANT(EFAULT) - #endif - #if defined(EFBIG) - CONSTANT(EFBIG) - #endif - #if defined(EHOSTUNREACH) - CONSTANT(EHOSTUNREACH) - #endif - #if defined(EIDRM) - CONSTANT(EIDRM) - #endif - #if defined(EILSEQ) - CONSTANT(EILSEQ) - #endif - #if defined(EINPROGRESS) - CONSTANT(EINPROGRESS) - #endif - #if defined(EINTR) - CONSTANT(EINTR) - #endif - #if defined(EINVAL) - CONSTANT(EINVAL) - #endif - #if defined(EIO) - CONSTANT(EIO) - #endif - #if defined(EISCONN) - CONSTANT(EISCONN) - #endif - #if defined(EISDIR) - CONSTANT(EISDIR) - #endif - #if defined(ELOOP) - CONSTANT(ELOOP) - #endif - #if defined(EMFILE) - CONSTANT(EMFILE) - #endif - #if defined(EMLINK) - CONSTANT(EMLINK) - #endif - #if defined(EMSGSIZE) - CONSTANT(EMSGSIZE) - #endif - #if defined(EMULTIHOP) - CONSTANT(EMULTIHOP) - #endif - #if defined(ENAMETOOLONG) - CONSTANT(ENAMETOOLONG) - #endif - #if defined(ENETDOWN) - CONSTANT(ENETDOWN) - #endif - #if defined(ENETRESET) - CONSTANT(ENETRESET) - #endif - #if defined(ENETUNREACH) - CONSTANT(ENETUNREACH) - #endif - #if defined(ENFILE) - CONSTANT(ENFILE) - #endif - #if defined(ENOBUFS) - CONSTANT(ENOBUFS) - #endif - #if defined(ENODATA) - CONSTANT(ENODATA) - #endif - #if defined(ENODEV) - CONSTANT(ENODEV) - #endif - #if defined(ENOENT) - CONSTANT(ENOENT) - #endif - #if defined(ENOEXEC) - CONSTANT(ENOEXEC) - #endif - #if defined(ENOLCK) - CONSTANT(ENOLCK) - #endif - #if defined(ENOLINK) - CONSTANT(ENOLINK) - #endif - #if defined(ENOMEM) - CONSTANT(ENOMEM) - #endif - #if defined(ENOMSG) - CONSTANT(ENOMSG) - #endif - #if defined(ENOPROTOOPT) - CONSTANT(ENOPROTOOPT) - #endif - #if defined(ENOSPC) - CONSTANT(ENOSPC) - #endif - #if defined(ENOSR) - CONSTANT(ENOSR) - #endif - #if defined(ENOSTR) - CONSTANT(ENOSTR) - #endif - #if defined(ENOSYS) - CONSTANT(ENOSYS) - #endif - #if defined(ENOTCONN) - CONSTANT(ENOTCONN) - #endif - #if defined(ENOTDIR) - CONSTANT(ENOTDIR) - #endif - #if defined(ENOTEMPTY) - CONSTANT(ENOTEMPTY) - #endif - #if defined(ENOTSOCK) - CONSTANT(ENOTSOCK) - #endif - #if defined(ENOTSUP) - CONSTANT(ENOTSUP) - #endif - #if defined(ENOTTY) - CONSTANT(ENOTTY) - #endif - #if defined(ENXIO) - CONSTANT(ENXIO) - #endif - #if defined(EOPNOTSUPP) - CONSTANT(EOPNOTSUPP) - #endif - #if defined(EOVERFLOW) - CONSTANT(EOVERFLOW) - #endif - #if defined(EPERM) - CONSTANT(EPERM) - #endif - #if defined(EPIPE) - CONSTANT(EPIPE) - #endif - #if defined(EPROTO) - CONSTANT(EPROTO) - #endif - #if defined(EPROTONOSUPPORT) - CONSTANT(EPROTONOSUPPORT) - #endif - #if defined(EPROTOTYPE) - CONSTANT(EPROTOTYPE) - #endif - #if defined(ERANGE) - CONSTANT(ERANGE) - #endif - #if defined(EROFS) - CONSTANT(EROFS) - #endif - #if defined(ESPIPE) - CONSTANT(ESPIPE) - #endif - #if defined(ESRCH) - CONSTANT(ESRCH) - #endif - #if defined(ESTALE) - CONSTANT(ESTALE) - #endif - #if defined(ETIME) - CONSTANT(ETIME) - #endif - #if defined(ETIMEDOUT) - CONSTANT(ETIMEDOUT) - #endif - #if defined(ETXTBSY) - CONSTANT(ETXTBSY) - #endif - #if defined(EWOULDBLOCK) - CONSTANT(EWOULDBLOCK) - #endif - #if defined(EXDEV) - CONSTANT(EXDEV) - #endif - - #if defined(SIGHUP) - CONSTANT(SIGHUP) - #endif - #if defined(SIGINT) - CONSTANT(SIGINT) - #endif - #if defined(SIGQUIT) - CONSTANT(SIGQUIT) - #endif - #if defined(SIGILL) - CONSTANT(SIGILL) - #endif - #if defined(SIGTRAP) - CONSTANT(SIGTRAP) - #endif - #if defined(SIGABRT) - CONSTANT(SIGABRT) - #endif - #if defined(SIGIOT) - CONSTANT(SIGIOT) - #endif - #if defined(SIGBUS) - CONSTANT(SIGBUS) - #endif - #if defined(SIGFPE) - CONSTANT(SIGFPE) - #endif - #if defined(SIGKILL) - CONSTANT(SIGKILL) - #endif - #if defined(SIGUSR1) - CONSTANT(SIGUSR1) - #endif - #if defined(SIGSEGV) - CONSTANT(SIGSEGV) - #endif - #if defined(SIGUSR2) - CONSTANT(SIGUSR2) - #endif - #if defined(SIGPIPE) - CONSTANT(SIGPIPE) - #endif - #if defined(SIGALRM) - CONSTANT(SIGALRM) - #endif - #if defined(SIGTERM) - CONSTANT(SIGTERM) - #endif - #if defined(SIGCHLD) - CONSTANT(SIGCHLD) - #endif - #if defined(SIGCONT) - CONSTANT(SIGCONT) - #endif - #if defined(SIGSTOP) - CONSTANT(SIGSTOP) - #endif - #if defined(SIGTSTP) - CONSTANT(SIGTSTP) - #endif - #if defined(SIGTTIN) - CONSTANT(SIGTTIN) - #endif - #if defined(SIGTTOU) - CONSTANT(SIGTTOU) - #endif - #if defined(SIGURG) - CONSTANT(SIGURG) - #endif - #if defined(SIGXCPU) - CONSTANT(SIGXCPU) - #endif - #if defined(SIGXFSZ) - CONSTANT(SIGXFSZ) - #endif - #if defined(SIGVTALRM) - CONSTANT(SIGVTALRM) - #endif - #if defined(SIGPROF) - CONSTANT(SIGPROF) - #endif - #if defined(SIGWINCH) - CONSTANT(SIGWINCH) - #endif - #if defined(SIGIO) - CONSTANT(SIGIO) - #endif - #if defined(SIGINFO) - CONSTANT(SIGINFO) - #endif - #if defined(SIGSYS) - CONSTANT(SIGSYS) - #endif - }; - #undef CONSTANT - - void CoreOS::cpus ( - const String& seq, - const CoreModule::Callback& callback - ) const { - this->core->dispatchEventLoop([=, this]() { - #if SOCKET_RUNTIME_PLATFORM_ANDROID - { - auto json = JSON::Object::Entries { - {"source", "os.cpus"}, - {"data", JSON::Array::Entries {}} - }; - - callback(seq, json, Post{}); - return; - } - #endif - - uv_cpu_info_t* infos = nullptr; - int count = 0; - int status = uv_cpu_info(&infos, &count); - - if (status != 0) { - auto json = JSON::Object::Entries { - {"source", "os.cpus"}, - {"err", JSON::Object::Entries { - {"message", uv_strerror(status)} - }} - }; - - callback(seq, json, Post{}); - return; - } - - JSON::Array::Entries entries(count); - for (int i = 0; i < count; ++i) { - auto info = infos[i]; - entries[i] = JSON::Object::Entries { - {"model", info.model}, - {"speed", info.speed}, - {"times", JSON::Object::Entries { - {"user", info.cpu_times.user}, - {"nice", info.cpu_times.nice}, - {"sys", info.cpu_times.sys}, - {"idle", info.cpu_times.idle}, - {"irq", info.cpu_times.irq} - }} - }; - } - - auto json = JSON::Object::Entries { - {"source", "os.cpus"}, - {"data", entries} - }; - - uv_free_cpu_info(infos, count); - callback(seq, json, Post{}); - }); - } - - void CoreOS::networkInterfaces ( - const String& seq, - const CoreModule::Callback& callback - ) const { - uv_interface_address_t *infos = nullptr; - StringStream value; - StringStream v4; - StringStream v6; - int count = 0; - int status = uv_interface_addresses(&infos, &count); - - if (status != 0) { - auto json = JSON::Object(JSON::Object::Entries { - {"source", "os.networkInterfaces"}, - {"err", JSON::Object::Entries { - {"type", "InternalError"}, - {"message", - String("Unable to get network interfaces: ") + String(uv_strerror(status)) - } - }} - }); - - return callback(seq, json, Post{}); - } - - JSON::Object::Entries ipv4; - JSON::Object::Entries ipv6; - JSON::Object::Entries data; - - for (int i = 0; i < count; ++i) { - uv_interface_address_t info = infos[i]; - struct sockaddr_in *addr = (struct sockaddr_in*) &info.address.address4; - char mac[18] = {0}; - snprintf(mac, 18, "%02x:%02x:%02x:%02x:%02x:%02x", - (unsigned char) info.phys_addr[0], - (unsigned char) info.phys_addr[1], - (unsigned char) info.phys_addr[2], - (unsigned char) info.phys_addr[3], - (unsigned char) info.phys_addr[4], - (unsigned char) info.phys_addr[5] - ); - - if (addr->sin_family == AF_INET) { - JSON::Object::Entries entries; - entries["internal"] = info.is_internal == 0 ? "false" : "true"; - entries["address"] = IP::addrToIPv4(addr); - entries["mac"] = String(mac, 17); - ipv4[String(info.name)] = entries; - } - - if (addr->sin_family == AF_INET6) { - JSON::Object::Entries entries; - entries["internal"] = info.is_internal == 0 ? "false" : "true"; - entries["address"] = IP::addrToIPv6((struct sockaddr_in6*) addr); - entries["mac"] = String(mac, 17); - ipv6[String(info.name)] = entries; - } - } - - uv_free_interface_addresses(infos, count); - - data["ipv4"] = ipv4; - data["ipv6"] = ipv6; - - auto json = JSON::Object::Entries { - {"source", "os.networkInterfaces"}, - {"data", data} - }; - - callback(seq, json, Post{}); - } - - void CoreOS::rusage ( - const String& seq, - const CoreModule::Callback& callback - ) const { - uv_rusage_t usage; - auto status = uv_getrusage(&usage); - - if (status != 0) { - auto json = JSON::Object::Entries { - {"source", "os.rusage"}, - {"err", JSON::Object::Entries { - {"message", uv_strerror(status)} - }} - }; - - callback(seq, json, Post{}); - return; - } - - auto json = JSON::Object::Entries { - {"source", "os.rusage"}, - {"data", JSON::Object::Entries { - {"ru_maxrss", usage.ru_maxrss} - }} - }; - - callback(seq, json, Post{}); - } - - void CoreOS::uname ( - const String& seq, - const CoreModule::Callback& callback - ) const { - uv_utsname_t uname; - auto status = uv_os_uname(&uname); - - if (status != 0) { - auto json = JSON::Object::Entries { - {"source", "os.uname"}, - {"err", JSON::Object::Entries { - {"message", uv_strerror(status)} - }} - }; - - callback(seq, json, Post{}); - return; - } - - auto json = JSON::Object::Entries { - {"source", "os.uname"}, - {"data", JSON::Object::Entries { - {"sysname", uname.sysname}, - {"release", uname.release}, - {"version", uname.version}, - {"machine", uname.machine} - }} - }; - - callback(seq, json, Post{}); - } - - void CoreOS::uptime ( - const String& seq, - const CoreModule::Callback& callback - ) const { - double uptime; - auto status = uv_uptime(&uptime); - - if (status != 0) { - auto json = JSON::Object::Entries { - {"source", "os.uptime"}, - {"err", JSON::Object::Entries { - {"message", uv_strerror(status)} - }} - }; - - callback(seq, json, Post{}); - return; - } - - auto json = JSON::Object::Entries { - {"source", "os.uptime"}, - {"data", uptime * 1000} // in milliseconds - }; - - callback(seq, json, Post{}); - } - - void CoreOS::hrtime ( - const String& seq, - const CoreModule::Callback& callback - ) const { - auto hrtime = uv_hrtime(); - auto bytes = toBytes(hrtime); - auto size = bytes.size(); - auto post = Post {}; - auto body = new char[size]{0}; - auto json = JSON::Object {}; - post.body.reset(body); - post.length = size; - memcpy(body, bytes.data(), size); - callback(seq, json, post); - } - - void CoreOS::availableMemory ( - const String& seq, - const CoreModule::Callback& callback - ) const { - auto memory = uv_get_available_memory(); - auto bytes = toBytes(memory); - auto size = bytes.size(); - auto post = Post {}; - auto body = new char[size]{0}; - auto json = JSON::Object {}; - post.body.reset(body); - post.length = size; - memcpy(body, bytes.data(), size); - callback(seq, json, post); - } - - void CoreOS::bufferSize ( - const String& seq, - CoreUDP::ID id, - size_t size, - int buffer, - const CoreModule::Callback& callback - ) const { - if (buffer == 0) { - buffer = CoreOS::SEND_BUFFER; - } else if (buffer == 1) { - buffer = CoreOS::RECV_BUFFER; - } - - this->core->dispatchEventLoop([=, this]() { - auto socket = this->core->udp.getSocket(id); - - if (socket == nullptr) { - auto json = JSON::Object::Entries { - {"source", "bufferSize"}, - {"err", JSON::Object::Entries { - {"id", std::to_string(id)}, - {"code", "NOT_FOUND_ERR"}, - {"type", "NotFoundError"}, - {"message", "No socket with specified id"} - }} - }; - - callback(seq, json, Post{}); - return; - } - - Lock lock(socket->mutex); - auto handle = (uv_handle_t*) &socket->handle; - auto err = buffer == RECV_BUFFER - ? uv_recv_buffer_size(handle, (int *) &size) - : uv_send_buffer_size(handle, (int *) &size); - - if (err < 0) { - auto json = JSON::Object::Entries { - {"source", "bufferSize"}, - {"err", JSON::Object::Entries { - {"id", std::to_string(id)}, - {"code", "NOT_FOUND_ERR"}, - {"type", "NotFoundError"}, - {"message", String(uv_strerror(err))} - }} - }; - - callback(seq, json, Post{}); - return; - } - - auto json = JSON::Object::Entries { - {"source", "bufferSize"}, - {"data", JSON::Object::Entries { - {"id", std::to_string(id)}, - {"size", (int) size} - }} - }; - - callback(seq, json, Post{}); - }); - } - - void CoreOS::constants ( - const String& seq, - const CoreModule::Callback& callback - ) const { - static const auto data = JSON::Object(OS_CONSTANTS); - static const auto json = JSON::Object::Entries { - {"source", "os.constants"}, - {"data", data} - }; - - callback(seq, json, Post {}); - } -} diff --git a/src/core/modules/os.hh b/src/core/modules/os.hh deleted file mode 100644 index af8266dfdd..0000000000 --- a/src/core/modules/os.hh +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef SOCKET_RUNTIME_CORE_MODULE_OS_H -#define SOCKET_RUNTIME_CORE_MODULE_OS_H - -#include "../module.hh" - -namespace SSC { - class Core; - class CoreOS : public CoreModule { - public: - static const int RECV_BUFFER = 1; - static const int SEND_BUFFER = 0; - - CoreOS (Core* core) - : CoreModule(core) - {} - - void constants ( - const String& seq, - const CoreModule::Callback& callback - ) const; - - void bufferSize ( - const String& seq, - uint64_t peerId, - size_t size, - int buffer, // RECV_BUFFER, SEND_BUFFER - const CoreModule::Callback& callback - ) const; - - void cpus ( - const String& seq, - const CoreModule::Callback& callback - ) const; - - void networkInterfaces ( - const String& seq, - const CoreModule::Callback& callback - ) const; - - void rusage ( - const String& seq, - const CoreModule::Callback& callback - ) const; - - void uname ( - const String& seq, - const CoreModule::Callback& callback - ) const; - - void uptime ( - const String& seq, - const CoreModule::Callback& callback - ) const; - - void hrtime ( - const String& seq, - const CoreModule::Callback& callback - ) const; - - void availableMemory ( - const String& seq, - const CoreModule::Callback& callback - ) const; - }; -} -#endif diff --git a/src/core/modules/permissions.hh b/src/core/modules/permissions.hh deleted file mode 100644 index b43616f880..0000000000 --- a/src/core/modules/permissions.hh +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef SOCKET_RUNTIME_CORE_MODULE_PERMISSIONS_H -#define SOCKET_RUNTIME_CORE_MODULE_PERMISSIONS_H - -#include "../module.hh" - -namespace SSC { - class CorePermissions : public CoreModule { - public: - CorePermissions (Core* core) - : CoreModule(core) - {} - - bool hasRuntimePermission (const String& permission) const; - - void query ( - const String& seq, - const String& name, - const Callback& callback - ) const; - - void request ( - const String& seq, - const String& name, - const Map& options, - const Callback& callback - ) const; - }; -} -#endif diff --git a/src/core/modules/platform.hh b/src/core/modules/platform.hh deleted file mode 100644 index 6fc80bd158..0000000000 --- a/src/core/modules/platform.hh +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef SOCKET_RUNTIME_CORE_MODULE_PLATFORM_H -#define SOCKET_RUNTIME_CORE_MODULE_PLATFORM_H - -#include "../module.hh" - -namespace SSC { - class Core; - class CorePlatform : public CoreModule { - public: - Atomic wasFirstDOMContentLoadedEventDispatched = false; - - #if SOCKET_RUNTIME_PLATFORM_ANDROID - Android::JVMEnvironment jvm; - Android::Activity activity = nullptr; - Android::ContentResolver contentResolver; - #endif - - CorePlatform (Core* core) - : CoreModule(core) - {} - - #if SOCKET_RUNTIME_PLATFORM_ANDROID - void configureAndroidContext ( - Android::JVMEnvironment jvm, - Android::Activity activity - ); - #endif - - void event ( - const String& seq, - const String& event, - const String& data, - const String& frameType, - const String& frameSource, - const CoreModule::Callback& callback - ); - - void openExternal ( - const String& seq, - const String& value, - const CoreModule::Callback& callback - ) const; - - void revealFile ( - const String& seq, - const String& value, - const CoreModule::Callback& callback - ) const; - }; -} -#endif diff --git a/src/core/modules/udp.hh b/src/core/modules/udp.hh deleted file mode 100644 index 7723fe2c31..0000000000 --- a/src/core/modules/udp.hh +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef SOCKET_RUNTIME_CORE_MODULE_UDP_H -#define SOCKET_RUNTIME_CORE_MODULE_UDP_H - -#include "../module.hh" -#include "../socket.hh" - -namespace SSC { - class Core; - class CoreUDP : public CoreModule { - public: - using ID = uint64_t; - using Sockets = std::map>; - - struct BindOptions { - String address; - int port; - bool reuseAddr = false; - }; - - struct ConnectOptions { - String address; - int port; - }; - - struct SendOptions { - String address = ""; - int port = 0; - SharedPointer bytes = nullptr; - size_t size = 0; - bool ephemeral = false; - }; - - Mutex mutex; - Sockets sockets; - - CoreUDP (Core* core) - : CoreModule(core) - {} - - void bind ( - const String& seq, - ID id, - const BindOptions& options, - const CoreModule::Callback& callback - ); - - void close ( - const String& seq, - ID id, - const CoreModule::Callback& callback - ); - - void connect ( - const String& seq, - ID id, - const ConnectOptions& options, - const CoreModule::Callback& callback - ); - - void disconnect ( - const String& seq, - ID id, - const CoreModule::Callback& callback - ); - - void getPeerName ( - const String& seq, - ID id, - const CoreModule::Callback& callback - ); - - void getSockName ( - const String& seq, - ID id, - const CoreModule::Callback& callback - ); - - void getState ( - const String& seq, - ID id, - const CoreModule::Callback& callback - ); - - void readStart ( - const String& seq, - ID id, - const CoreModule::Callback& callback - ); - - void readStop ( - const String& seq, - ID id, - const CoreModule::Callback& callback - ); - - void send ( - const String& seq, - ID id, - const SendOptions& options, - const CoreModule::Callback& callback - ); - - void resumeAllSockets (); - void pauseAllSockets (); - bool hasSocket (ID id); - void removeSocket (ID id); - void removeSocket (ID id, bool autoClose); - SharedPointer getSocket (ID id); - SharedPointer createSocket (socket_type_t type, ID id); - SharedPointer createSocket (socket_type_t type, ID id, bool isEphemeral); - }; -} -#endif diff --git a/src/core/post.hh b/src/core/post.hh deleted file mode 100644 index fda395226d..0000000000 --- a/src/core/post.hh +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef SOCKET_RUNTIME_CORE_POST_H -#define SOCKET_RUNTIME_CORE_POST_H - -#include "../platform/types.hh" - -namespace SSC { - struct Post { - using EventStreamCallback = Function; - - using ChunkStreamCallback = Function; - - uint64_t id = 0; - uint64_t ttl = 0; - SharedPointer body = nullptr; - size_t length = 0; - String headers = ""; - String workerId = ""; - SharedPointer eventStream = nullptr; - SharedPointer chunkStream = nullptr; - }; - - using Posts = std::map; -} -#endif diff --git a/src/core/unique_client.hh b/src/core/unique_client.hh deleted file mode 100644 index 8c6697278b..0000000000 --- a/src/core/unique_client.hh +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef SOCKET_RUNTIME_UNIQUE_CLIENT_H -#define SOCKET_RUNTIME_UNIQUE_CLIENT_H - -#include "../platform/platform.hh" - -namespace SSC { - struct UniqueClient { - using ID = uint64_t; - ID id = 0; - int index = 0; - }; -} -#endif diff --git a/src/core/url.cc b/src/core/url.cc deleted file mode 100644 index 2445073c92..0000000000 --- a/src/core/url.cc +++ /dev/null @@ -1,461 +0,0 @@ -#include - -#include "codec.hh" -#include "debug.hh" -#include "url.hh" - -namespace SSC { - const URL::Components URL::Components::parse (const String& url) { - URL::Components components; - components.originalURL = url; - auto input = url; - - if (input.starts_with("./")) { - input = input.substr(1); - } - - if (!input.starts_with("/")) { - const auto colon = input.find(':'); - - if (colon != String::npos) { - components.scheme = input.substr(0, colon); - input = input.substr(colon + 1, input.size()); - - if (input.starts_with("//")) { - input = input.substr(2, input.size()); - - const auto slash = input.find("/"); - if (slash != String::npos) { - components.authority = input.substr(0, slash); - input = input.substr(slash, input.size()); - } else { - const auto questionMark = input.find("?"); - const auto fragment = input.find("#"); - if (questionMark != String::npos & fragment != String::npos) { - if (questionMark < fragment) { - components.authority = input.substr(0, questionMark); - input = input.substr(questionMark, input.size()); - } else { - components.authority = input.substr(0, fragment); - input = input.substr(fragment, input.size()); - } - } else if (questionMark != String::npos) { - components.authority = input.substr(0, questionMark); - input = input.substr(questionMark, input.size()); - } else if (fragment != String::npos) { - components.authority = input.substr(0, fragment); - input = input.substr(fragment, input.size()); - } else { - components.authority = input; - components.pathname = "/"; - } - } - } - } - } - - if (components.pathname.size() == 0) { - const auto questionMark = input.find("?"); - const auto fragment = input.find("#"); - - if (questionMark != String::npos && fragment != String::npos) { - if (questionMark < fragment) { - components.pathname = input.substr(0, questionMark); - components.query = input.substr(questionMark + 1, fragment - questionMark - 1); - components.fragment = input.substr(fragment + 1, input.size()); - } else { - components.pathname = input.substr(0, fragment); - components.fragment = input.substr(fragment + 1, input.size()); - } - } else if (questionMark != String::npos) { - components.pathname = input.substr(0, questionMark); - components.query = input.substr(questionMark + 1, input.size()); - } else if (fragment != String::npos) { - components.pathname = input.substr(0, fragment); - components.fragment = input.substr(fragment + 1, input.size()); - } else { - components.pathname = input; - } - - if (!components.pathname.starts_with("/")) { - components.pathname = "/" + components.pathname; - } - } - - return components; - } - - URL::PathComponents::PathComponents (const String& pathname) { - this->set(pathname); - } - - void URL::PathComponents::set (const String& pathname) { - const auto parts = split(pathname, "/"); - for (const auto& part : parts) { - const auto value = trim(part); - if (value.size() > 0) { - this->parts.push_back(value); - } - } - } - - const String URL::PathComponents::operator[] (const size_t index) const { - return this->parts[index]; - } - - const String& URL::PathComponents::operator[] (const size_t index) { - return this->parts[index]; - } - - const String& URL::PathComponents:: at (const size_t index) const { - return this->parts.at(index); - } - - const String URL::PathComponents::str () const noexcept { - return "/" + join(this->parts, "/"); - } - - const URL::PathComponents::Iterator URL::PathComponents::begin () const noexcept { - return this->parts.begin(); - } - - const URL::PathComponents::Iterator URL::PathComponents::end () const noexcept { - return this->parts.end(); - } - - const size_t URL::PathComponents::size () const noexcept { - return this->parts.size(); - } - - const bool URL::PathComponents::empty () const noexcept { - return this->parts.empty(); - } - - template - const T URL::PathComponents::get (const size_t index) const { - if (std::is_same::value) { - return std::stoull(this->at(index)); - } - - if (std::is_same::value) { - return std::stoll(this->at(index)); - } - - if (std::is_same::value) { - return std::stoul(this->at(index)); - } - - if (std::is_same::value) { - return std::stol(this->at(index)); - } - - if (std::is_same::value) { - return std::stoul(this->at(index)); - } - - if (std::is_same::value) { - return std::stol(this->at(index)); - } - - if (std::is_same::value) { - return std::stoul(this->at(index)); - } - - if (std::is_same::value) { - return std::stod(this->at(index)); - } - - if (std::is_same::value) { - if (std::stod(this->at(index)) == 0) { - return false; - } - - return true; - } - - throw new Error("unhandled type in URL::PathComponents::get"); - } - - template const uint64_t URL::PathComponents::get (const size_t index) const; - template const int64_t URL::PathComponents::get (const size_t index) const; - template const uint32_t URL::PathComponents::get (const size_t index) const; - template const int32_t URL::PathComponents::get (const size_t index) const; - template const uint16_t URL::PathComponents::get (const size_t index) const; - template const int16_t URL::PathComponents::get (const size_t index) const; - template const uint8_t URL::PathComponents::get (const size_t index) const; - template const int8_t URL::PathComponents::get (const size_t index) const; - template const bool URL::PathComponents::get (const size_t index) const; - - URL::SearchParams::SearchParams (const String& input) { - const auto query = input.starts_with("?") - ? input.substr(1) - : input; - - for (const auto& entry : split(query, '&')) { - const auto parts = split(entry, '='); - if (parts.size() == 2) { - const auto key = decodeURIComponent(trim(parts[0])); - const auto value = decodeURIComponent(trim(parts[1])); - this->set(key, value); - } - } - } - - URL::SearchParams::SearchParams (const SearchParams& input) { - for (const auto& entry : input) { - this->set(entry.first, entry.second); - } - } - - URL::SearchParams::SearchParams (const Map& input) { - for (const auto& entry : input) { - this->set(entry.first, entry.second); - } - } - - URL::SearchParams::SearchParams (const JSON::Object& input) { - for (const auto& entry : input) { - this->set(entry.first, entry.second); - } - } - - const String URL::SearchParams::str () const { - Vector components; - for (const auto& entry : *this) { - const auto parts = Vector { - entry.first, - entry.second.str() - }; - - components.push_back(join(parts, "=")); - } - - return join(components, "&"); - } - - URL::Builder& URL::Builder::setProtocol (const String& protocol) { - this->protocol = protocol; - return *this; - } - - URL::Builder& URL::Builder::setUsername (const String& username) { - this->username = username; - return *this; - } - - URL::Builder& URL::Builder::setPassword (const String& password) { - this->password = password; - return *this; - } - - URL::Builder& URL::Builder::setHostname (const String& hostname) { - this->hostname = hostname; - return *this; - } - - URL::Builder& URL::Builder::setPort (const String& port) { - this->port = port; - return *this; - } - - URL::Builder& URL::Builder::setPort (const int port) { - this->port = std::to_string(port); - return *this; - } - - URL::Builder& URL::Builder::setPathname (const String& pathname) { - this->pathname = pathname; - return *this; - } - - URL::Builder& URL::Builder::setQuery (const String& query) { - this->search = "?" + query; - return *this; - } - - URL::Builder& URL::Builder::setSearch (const String& search) { - this->search = search; - return *this; - } - - URL::Builder& URL::Builder::setHash (const String& hash) { - this->hash = hash; - return *this; - } - - URL::Builder& URL::Builder::setFragment (const String& fragment) { - this->hash = "#" + fragment; - return *this; - } - - URL::Builder& URL::Builder::setSearchParam (const String& key, const String& value) { - return this->setSearchParams(Map {{ key, value }}); - } - - URL::Builder& URL::Builder::setSearchParam (const String& key, const JSON::Any& value) { - if (JSON::typeof(value) == "string" || JSON::typeof(value) == "number" || JSON::typeof(value) == "boolean") { - return this->setSearchParam(key, value.str()); - } - - return *this; - } - - URL::Builder& URL::Builder::setSearchParams (const Map& params) { - if (params.size() > 0) { - if (!this->search.starts_with("?")) { - this->search = "?"; - } else if (this->search.size() > 0) { - this->search += "&"; - } - - for (const auto& entry : params) { - this->search = entry.first + "=" + entry.second + "&"; - } - } - - if (this->search.ends_with("&")) { - this->search = this->search.substr(0, this->search.size() - 1); - } - - return *this; - } - - URL URL::Builder::build () const { - StringStream stream; - - if (this->protocol.size() == 0) { - return String(""); - } - - stream << this->protocol << ":"; - - if ( - (this->username.size() > 0 || this->password.size() > 0) && - this->hostname.size() > 0 - ) { - stream << "//"; - if (this->username.size() > 0) { - stream << this->username; - if (this->password.size() > 0) { - stream << ":" << this->password; - } - - stream << "@" << this->hostname; - if (this->port.size() > 0) { - stream << ":" << this->port; - } - } - } - - if (this->hostname.size() > 0 && this->pathname.size() > 0) { - if (!this->pathname.starts_with("/")) { - stream << "/"; - } - } - - stream << this->pathname << this->search << this->hash; - - return stream.str(); - } - - URL::URL (const JSON::Object& json) - : URL(json["href"].str()) - {} - - URL::URL (const String& href) { - if (href.size() > 0) { - this->set(href); - } - } - - void URL::set (const String& href) { - const auto components = URL::Components::parse(href); - - this->scheme = components.scheme; - this->pathname = components.pathname; - this->query = components.query; - this->fragment = components.fragment; - this->search = this->query.size() > 0 ? "?" + this->query : ""; - this->hash = this->fragment.size() > 0 ? "#" + this->fragment : ""; - - if (components.scheme.size() > 0) { - this->protocol = components.scheme + ":"; - } - - const auto authorityParts = components.authority.size() > 0 - ? split(components.authority, '@') - : Vector {}; - - if (authorityParts.size() == 2) { - const auto userParts = split(authorityParts[0], ':'); - - if (userParts.size() == 2) { - this->username = userParts[0]; - this->password = userParts[1]; - } else if (userParts.size() == 1) { - this->username = userParts[0]; - } - - const auto hostParts = split(authorityParts[1], ':'); - if (hostParts.size() > 1) { - this->port = hostParts[1]; - } - - if (hostParts.size() > 0) { - this->hostname = hostParts[0]; - } - } else if (authorityParts.size() == 1) { - const auto hostParts = split(authorityParts[0], ':'); - if (hostParts.size() > 1) { - this->port = hostParts[1]; - } - - if (hostParts.size() > 0) { - this->hostname = hostParts[0]; - } - } - - if (this->protocol.size() > 0) { - if (this->hostname.size() > 0) { - this->origin = this->protocol + "//" + this->hostname; - } else { - this->origin = this->protocol + this->pathname; - } - - this->href = this->origin + this->pathname + this->search + this->hash; - } - - if (this->query.size() > 0) { - for (const auto& entry : split(this->query, '&')) { - const auto parts = split(entry, '='); - if (parts.size() == 2) { - const auto key = decodeURIComponent(trim(parts[0])); - const auto value = decodeURIComponent(trim(parts[1])); - this->searchParams.set(key, value); - } - } - } - - if (this->pathname.size() > 0) { - this->pathComponents.set(this->pathname); - } - } - - const String URL::str () const { - return this->href; - } - - const JSON::Object URL::json () const { - return JSON::Object::Entries { - {"href", this->href}, - {"origin", this->origin}, - {"protocol", this->protocol}, - {"username", this->username}, - {"password", this->password}, - {"hostname", this->hostname}, - {"pathname", this->pathname}, - {"search", this->search}, - {"hash", this->hash} - }; - } -} diff --git a/src/core/url.hh b/src/core/url.hh deleted file mode 100644 index fc07d209fb..0000000000 --- a/src/core/url.hh +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef SOCKET_RUNTIME_CORE_URL_H -#define SOCKET_RUNTIME_CORE_URL_H - -#include "json.hh" - -namespace SSC { - struct URL { - struct Components { - String originalURL = ""; - String scheme = ""; - String authority = ""; - String pathname = ""; - String query = ""; - String fragment = ""; - - static const Components parse (const String& url); - }; - - struct PathComponents { - using Iterator = Vector::const_iterator; - using const_iterator = Vector::const_iterator; - - Vector parts; - - PathComponents () = default; - PathComponents (const String& pathname); - const String operator[] (const size_t index) const; - const String& operator[] (const size_t index); - - void set (const String& pathname); - const String& at (const size_t index) const; - const String str () const noexcept; - const Iterator begin () const noexcept; - const size_t size () const noexcept; - const Iterator end () const noexcept; - const bool empty () const noexcept; - - template - const T get (const size_t index) const; - }; - - class SearchParams : public JSON::Object { - public: - class Value : public JSON::Any { - public: - Value (const Any& value) - : JSON::Any(value.str()) - {} - }; - - using Entries = JSON::Object::Entries; - SearchParams () = default; - SearchParams (const SearchParams&); - SearchParams (const String&); - SearchParams (const Map&); - SearchParams (const JSON::Object&); - const String str () const; - }; - - struct Builder { - String protocol = ""; - String username = ""; - String password = ""; - String hostname = ""; - String port = ""; - String pathname = ""; - String search = ""; // includes '?' and 'query' if 'query' is not empty - String hash = ""; // include '#' and 'fragment' if 'fragment' is not empty - - Builder& setProtocol (const String& protocol); - Builder& setUsername (const String& username); - Builder& setPassword (const String& password); - Builder& setHostname (const String& hostname); - Builder& setPort (const String& port); - Builder& setPort (const int port); - Builder& setPathname (const String& pathname); - Builder& setQuery (const String& query); - Builder& setSearch (const String& search); - Builder& setHash (const String& hash); - Builder& setFragment (const String& fragment); - Builder& setSearchParam (const String& key, const String& value); - Builder& setSearchParam (const String& key, const JSON::Any& value); - Builder& setSearchParams (const Map& params); - - URL build () const; - }; - - // core properties - String href = ""; - String origin = ""; - String protocol = ""; - String username = ""; - String password = ""; - String hostname = ""; - String port = ""; - String pathname = ""; - String search = ""; // includes '?' and 'query' if 'query' is not empty - String hash = ""; // include '#' and 'fragment' if 'fragment' is not empty - - // extra properties - String scheme; - String fragment; - String query; - - SearchParams searchParams; - - PathComponents pathComponents; - - URL () = default; - URL (const String& href); - URL (const JSON::Object& json); - - void set (const String& href); - void set (const JSON::Object& json); - const String str () const; - const JSON::Object json () const; - }; -} -#endif diff --git a/src/desktop/extension/linux.cc b/src/desktop/extension/linux.cc index c85ced1ae9..fad0051a15 100644 --- a/src/desktop/extension/linux.cc +++ b/src/desktop/extension/linux.cc @@ -1,9 +1,9 @@ #define SOCKET_RUNTIME_DESKTOP_EXTENSION 1 #include "../../platform/platform.hh" -#include "../../core/resource.hh" -#include "../../core/debug.hh" -#include "../../core/trace.hh" -#include "../../core/url.hh" +#include "../../runtime/resource.hh" +#include "../../runtime/debug.hh" +#include "../../runtime/trace.hh" +#include "../../runtime/url.hh" #include "../../app/app.hh" #include "../extension.hh" @@ -26,7 +26,7 @@ extern "C" { if (sharedBridge == nullptr) { g_object_ref(context); auto options = IPC::Bridge::Options(-1, app->userConfig); - sharedBridge = std::make_shared(app->core, options); + sharedBridge = std::make_shared(app->runtime, options); sharedBridge->dispatchFunction = [](auto callback) { callback(); }; @@ -129,11 +129,11 @@ extern "C" { if (message->get("__sync__") == "true") { auto bridge = getSharedBridge(context); auto app = App::sharedApplication(); - auto semaphore = new std::binary_semaphore{0}; + auto semaphore = new BinarySemaphore(0); IPC::Result returnResult; - auto routed = bridge->route( + const auto routed = bridge->route( message->str(), message->buffer.bytes, message->buffer.size, @@ -266,8 +266,22 @@ extern "C" { } } - Core::Options options; + Runtime::Options options; options.dedicatedLoopThread = true; + + options.features.usePlatform = true; + options.features.useTimers = true; + options.features.useFS = true; + + options.features.useNotifications = false; + options.features.useNetworkStatus = false; + options.features.usePermissions = false; + options.features.useGeolocation = false; + options.features.useConduit = false; + options.features.useUDP = false; + options.features.useDNS = false; + options.features.useAI = false; + auto userConfig = getUserConfig(); auto cwd = userConfig["web-process-extension_cwd"]; @@ -278,7 +292,7 @@ extern "C" { static App app( App::DEFAULT_INSTANCE_ID, - std::move(std::make_shared(options)) + std::move(std::make_shared(options)) ); } diff --git a/src/desktop/main.cc b/src/desktop/main.cc index 7c5375b9b5..bb6a526663 100644 --- a/src/desktop/main.cc +++ b/src/desktop/main.cc @@ -241,6 +241,9 @@ BOOL registerWindowsURISchemeInRegistry () { } #endif +static const String OK_STATE = "0"; +static const String ERROR_STATE = "1"; + // // the MAIN macro provides a cross-platform program entry point. // it makes argc and argv uniformly available. It provides "instanceId" @@ -262,9 +265,6 @@ MAIN { const String devHost = getDevHost(); const auto devPort = getDevPort(); - const String OK_STATE = "0"; - const String ERROR_STATE = "1"; - auto cwd = app.getcwd(); app.userConfig = userConfig; @@ -794,7 +794,7 @@ MAIN { // callback doesnt need to dispatch because it's already in the // main thread. // - const auto onMessage = [&](const auto& output) { + const auto onMessage = [cmd, &killProcess](const auto& output) { const auto message = IPC::Message(output, true); auto window = app.windowManager.getWindow(message.index); diff --git a/src/extension/extension.hh b/src/extension/extension.hh index 670878a0c7..c835e61482 100644 --- a/src/extension/extension.hh +++ b/src/extension/extension.hh @@ -69,7 +69,7 @@ namespace SSC { } }; - using PolicyMap = std::map; + using PolicyMap = Map; const Extension* extension = nullptr; IPC::Router* router = nullptr; @@ -82,7 +82,7 @@ namespace SSC { Error error; std::atomic retain_count = 0; PolicyMap policies; - Map config; + Map config; Context () = default; Context (const Extension* extension); @@ -100,11 +100,11 @@ namespace SSC { bool isAllowed (const String& name) const; }; - using Map = std::map>; + using Map = SSC::Map>; using Entry = std::shared_ptr; using Initializer = std::function; using Deinitializer = std::function; - using RouterContexts = std::map; + using RouterContexts = SSC::Map; RouterContexts contexts; Context context; @@ -266,13 +266,13 @@ extern "C" { sapi_context_t* context = nullptr; sapi_json_raw ( const char* source - ) : SSC::JSON::Raw(source) + ) : SSC::JSON::Raw(SSC::String(source)) {} sapi_json_raw ( sapi_context_t* ctx, const char* source - ) : context(ctx), SSC::JSON::Raw(source) + ) : context(ctx), SSC::JSON::Raw(SSC::String(source)) {} }; }; diff --git a/src/extension/ipc.cc b/src/extension/ipc.cc index d3b45e9f3d..2d3ed0f198 100644 --- a/src/extension/ipc.cc +++ b/src/extension/ipc.cc @@ -20,11 +20,10 @@ bool sapi_ipc_router_map ( } ctx->router->map(name, [ctx, data, callback]( - auto& message, + auto message, auto router, auto reply ) mutable { - auto msg = SSC::IPC::Message(message); auto context = sapi_context_create(ctx, true); if (context == nullptr) { return; @@ -34,7 +33,7 @@ bool sapi_ipc_router_map ( context->internal = new SSC::IPC::Router::ReplyCallback(reply); callback( context, - (sapi_ipc_message_t*) &msg, + (sapi_ipc_message_t*) &message, reinterpret_cast(&router) ); }); @@ -73,7 +72,7 @@ uint64_t sapi_ipc_router_listen ( } return ctx->router->listen(name, [data, callback]( - auto& message, + auto message, auto router, auto reply ) mutable { @@ -129,7 +128,7 @@ bool sapi_ipc_reply (const sapi_ipc_result_t* result) { auto fn = reinterpret_cast(internal); if (fn != nullptr) { - (*fn)(*result); + (*fn)(*reinterpret_cast(result)); success = true; delete fn; } @@ -470,8 +469,8 @@ const char* sapi_ipc_message_get ( const sapi_ipc_message_t* message, const char* key ) { - if (!message || !key || !message->args.contains(key)) return nullptr; - auto& value = message->args.at(key); + if (!message || !key || !message->contains(key)) return nullptr; + const auto& value = message->at(key); if (value.size() == 0) return nullptr; return value.c_str(); } diff --git a/src/extension/json.cc b/src/extension/json.cc index 7386ac87a2..ce7c112b11 100644 --- a/src/extension/json.cc +++ b/src/extension/json.cc @@ -138,7 +138,7 @@ sapi_json_any_t* sapi_json_object_get ( const char* key ) { if (json->has(key)) { - auto pointer = json->data.at(key).pointer.get(); + auto pointer = json->data.at(key).data.get(); return reinterpret_cast(pointer); } @@ -213,7 +213,7 @@ sapi_json_any_t* sapi_json_array_get ( unsigned int index ) { if (json->has(index)) { - auto pointer = json->data.at(index).pointer.get(); + auto pointer = json->data.at(index).data.get(); return reinterpret_cast(pointer); } @@ -223,6 +223,6 @@ sapi_json_any_t* sapi_json_array_get ( sapi_json_any_t* sapi_json_array_pop ( sapi_json_array_t* json ) { - auto pointer = json->pop().pointer.get(); + auto pointer = json->pop().data.get(); return reinterpret_cast(pointer); } diff --git a/src/ipc/bridge.hh b/src/ipc/bridge.hh deleted file mode 100644 index 33aefc66a8..0000000000 --- a/src/ipc/bridge.hh +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef SOCKET_RUNTIME_IPC_BRIDGE_H -#define SOCKET_RUNTIME_IPC_BRIDGE_H - -#include "../core/core.hh" -#include "../core/options.hh" -#include "../core/webview.hh" - -#include "client.hh" -#include "preload.hh" -#include "navigator.hh" -#include "router.hh" -#include "scheme_handlers.hh" - -namespace SSC::IPC { - class Bridge { - public: - using EvaluateJavaScriptFunction = Function; - using NavigateFunction = Function; - using DispatchCallback = Function; - using DispatchFunction = Function; - - struct Options : public SSC::Options { - Map userConfig = {}; - Preload::Options preload; - uint64_t id = 0; - int index = 0; - Options ( - int index, - const Map& userConfig = {}, - const Preload::Options& preload = {}, - uint64_t id = 0 - ); - }; - - static Vector getInstances(); - - const CoreNetworkStatus::Observer networkStatusObserver; - const CoreGeolocation::PermissionChangeObserver geolocationPermissionChangeObserver; - const CoreNotifications::PermissionChangeObserver notificationsPermissionChangeObserver; - const CoreNotifications::NotificationResponseObserver notificationResponseObserver; - const CoreNotifications::NotificationPresentedObserver notificationPresentedObserver; - - EvaluateJavaScriptFunction evaluateJavaScriptFunction = nullptr; - NavigateFunction navigateFunction = nullptr; - DispatchFunction dispatchFunction = nullptr; - - Bluetooth bluetooth; - Client client = {}; - Map userConfig; - Navigator navigator; - Router router; - SchemeHandlers schemeHandlers; - - SharedPointer core = nullptr; - uint64_t id = 0; - - #if SOCKET_RUNTIME_PLATFORM_ANDROID - bool isAndroidEmulator = false; - #endif - - Bridge (SharedPointercore, const Options& options); - Bridge () = delete; - Bridge (const Bridge&) = delete; - Bridge (Bridge&&) = delete; - ~Bridge (); - - Bridge& operator = (const Bridge&) = delete; - Bridge& operator = (Bridge&&) = delete; - - void init (); - void configureWebView (WebView* webview); - void configureSchemeHandlers (const SchemeHandlers::Configuration& configuration); - void configureNavigatorMounts (); - - bool route (const String& uri, SharedPointer bytes, size_t size); - bool route ( - const String& uri, - SharedPointer bytes, - size_t size, - Router::ResultCallback - ); - - bool evaluateJavaScript (const String& source); - bool dispatch (const DispatchCallback& callback); - bool navigate (const String& url); - bool emit (const String& name, const String& data = ""); - bool emit (const String& name, const JSON::Any& json = {}); - bool send (const Message::Seq& seq, const String& data, const Post& post = {}); - bool send (const Message::Seq& seq, const JSON::Any& json, const Post& post = {}); - }; -} -#endif diff --git a/src/ipc/client.hh b/src/ipc/client.hh deleted file mode 100644 index 6db3f1b66d..0000000000 --- a/src/ipc/client.hh +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef SOCKET_RUNTIME_IPC_CLIENT_H -#define SOCKET_RUNTIME_IPC_CLIENT_H - -#include "../core/unique_client.hh" -#include "preload.hh" - -namespace SSC::IPC { - struct Client : public UniqueClient { - using ID = UniqueClient::ID; - ID id = 0; - IPC::Preload preload = {}; - }; -} -#endif diff --git a/src/ipc/ipc.hh b/src/ipc/ipc.hh deleted file mode 100644 index 86f75abbe1..0000000000 --- a/src/ipc/ipc.hh +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef SOCKET_RUNTIME_IPC_H -#define SOCKET_RUNTIME_IPC_H - -#include "bridge.hh" -#include "client.hh" -#include "message.hh" -#include "navigator.hh" -#include "result.hh" -#include "router.hh" -#include "scheme_handlers.hh" - -#endif diff --git a/src/ipc/message.cc b/src/ipc/message.cc deleted file mode 100644 index 2331b32747..0000000000 --- a/src/ipc/message.cc +++ /dev/null @@ -1,95 +0,0 @@ -#include "message.hh" - -namespace SSC::IPC { - Message::Message (const Message& message) - : value(message.value), - index(message.index), - name(message.name), - seq(message.seq), - uri(message.uri), - args(message.args), - isHTTP(message.isHTTP), - cancel(message.cancel), - buffer(message.buffer), - client(message.client) - {} - - Message::Message (const String& source) - : Message(source, false) - {} - - Message::Message (const String& source, bool decodeValues) { - String str = source; - uri = str; - - // bail if missing protocol prefix - if (str.find("ipc://") == -1) return; - - // bail if malformed - if (str.compare("ipc://") == 0) return; - if (str.compare("ipc://?") == 0) return; - if (str.compare("ipc:///?") == 0) return; - - String query; - String path; - - auto raw = split(str, '?'); - path = raw[0]; - if (raw.size() > 1) query = raw[1]; - - auto parts = split(path, '/'); - if (parts.size() > 1) name = parts[1]; - - if (raw.size() != 2) return; - auto pairs = split(raw[1], '&'); - - for (auto& rawPair : pairs) { - auto pair = split(rawPair, '='); - if (pair.size() <= 1) continue; - - if (pair[0].compare("index") == 0) { - try { - this->index = std::stoi(pair[1].size() > 0 ? pair[1] : "0"); - } catch (...) { - debug("SSC:IPC::Message: Warning: received non-integer index"); - } - } - - if (pair[0].compare("value") == 0) { - this->value = decodeValues ? decodeURIComponent(pair[1]) : pair[1]; - } - - if (pair[0].compare("seq") == 0) { - this->seq = decodeURIComponent(pair[1]); - } - - if (decodeValues) { - this->args[pair[0]] = decodeURIComponent(pair[1]); - } else { - this->args[pair[0]] = pair[1]; - } - } - } - - bool Message::has (const String& key) const { - return ( - this->args.contains(key) && - this->args.at(key).size() > 0 - ); - } - - String Message::get (const String& key) const { - return this->get(key, ""); - } - - String Message::get (const String& key, const String &fallback) const { - if (key == "value") return this->value; - return this->args.contains(key) - ? decodeURIComponent(args.at(key)) - : fallback; - } - - const Map Message::dump () const { - return this->args; - } -} diff --git a/src/ipc/message.hh b/src/ipc/message.hh deleted file mode 100644 index 3b42962fd3..0000000000 --- a/src/ipc/message.hh +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef SOCKET_RUNTIME_IPC_MESSAGE_H -#define SOCKET_RUNTIME_IPC_MESSAGE_H - -#include "../core/core.hh" -#include "client.hh" - -namespace SSC::IPC { - struct MessageBuffer { - size_t size = 0; - SharedPointer bytes = nullptr; - - MessageBuffer () = default; - MessageBuffer (SharedPointer bytes, size_t size) - : size(size), - bytes(bytes) - {} - - #if SOCKET_RUNTIME_PLATFORM_WINDOWS - ICoreWebView2SharedBuffer* shared_buf = nullptr; - MessageBuffer (ICoreWebView2SharedBuffer* buf, size_t size) - : size(size), - shared_buf(buf) - { - BYTE* b = reinterpret_cast(bytes.get()); - HRESULT r = buf->get_Buffer(&b); - if (r != S_OK) { - // TODO(trevnorris): Handle this - } - } - #endif - }; - - struct MessageCancellation { - void (*handler)(void*) = nullptr; - void *data = nullptr; - }; - - class Message { - public: - using Seq = String; - MessageBuffer buffer; - Client client; - - String value = ""; - String name = ""; - String uri = ""; - int index = -1; - Seq seq = ""; - Map args = {}; - bool isHTTP = false; - SharedPointer cancel = nullptr; - - Message () = default; - Message (const Message& message); - Message (const String& source, bool decodeValues); - Message (const String& source); - bool has (const String& key) const; - String get (const String& key) const; - String get (const String& key, const String& fallback) const; - String str () const { return this->uri; } - const Map dump () const; - const char* c_str () const { return this->uri.c_str(); } - }; -} -#endif diff --git a/src/ipc/result.hh b/src/ipc/result.hh deleted file mode 100644 index 45bf0affc1..0000000000 --- a/src/ipc/result.hh +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef SOCKET_RUNTIME_IPC_RESULT_H -#define SOCKET_RUNTIME_IPC_RESULT_H - -#include "../core/core.hh" -#include "message.hh" - -namespace SSC::IPC { - class Result { - public: - class Err { - public: - Message message; - Message::Seq seq; - JSON::Any value; - Err () = default; - Err (const Message&, const char*); - Err (const Message&, const String&); - Err (const Message&, JSON::Any); - }; - - class Data { - public: - Message message; - Message::Seq seq; - JSON::Any value; - Post post; - - Data () = default; - Data (const Message&, JSON::Any); - Data (const Message&, JSON::Any, Post); - }; - - Message message; - Message::Seq seq = "-1"; - uint64_t id = 0; - String source = ""; - String token = ""; - JSON::Any value = nullptr; - JSON::Any data = nullptr; - JSON::Any err = nullptr; - Headers headers; - Post post; - - Result () = default; - Result (const Result&) = default; - Result (const JSON::Any, const String& token = ""); - Result (const Err error); - Result (const Data data); - Result (const Message::Seq&, const Message&); - Result (const Message::Seq&, const Message&, JSON::Any); - Result (const Message::Seq&, const Message&, JSON::Any, Post); - String str () const; - JSON::Any json () const; - }; -} -#endif diff --git a/src/ipc/router.hh b/src/ipc/router.hh deleted file mode 100644 index c3ba108d83..0000000000 --- a/src/ipc/router.hh +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef SOCKET_RUNTIME_IPC_ROUTER_H -#define SOCKET_RUNTIME_IPC_ROUTER_H - -#include "../core/core.hh" -#include "message.hh" -#include "result.hh" - -namespace SSC::IPC { - class Bridge; - class Router { - public: - using ReplyCallback = Function; - using ResultCallback = Function; - using MessageCallback = Function; - - struct MessageCallbackContext { - bool async = true; - MessageCallback callback; - }; - - struct MessageCallbackListenerContext { - uint64_t token; - MessageCallback callback; - }; - - using Table = std::map; - using Listeners = std::map>; - - private: - Table preserved; - - public: - Listeners listeners; - Mutex mutex; - Table table; - - Bridge *bridge = nullptr; - - Router () = default; - Router (Bridge* bridge); - Router (const Router&) = delete; - Router (const Router&&) = delete; - Router (Router&&) = delete; - - void init (); - void mapRoutes (); - void preserveCurrentTable (); - uint64_t listen (const String& name, const MessageCallback& callback); - bool unlisten (const String& name, uint64_t token); - void map (const String& name, const MessageCallback& callback); - void map (const String& name, bool async, const MessageCallback& callback); - void unmap (const String& name); - bool invoke (const String& uri, const ResultCallback& callback); - bool invoke (const String& uri, SharedPointer bytes, size_t size); - bool invoke ( - const String& uri, - SharedPointer bytes, - size_t size, - const ResultCallback& callback - ); - - bool invoke ( - const Message& message, - SharedPointer bytes, - size_t size, - const ResultCallback& callback - ); - }; -} -#endif diff --git a/src/runtime.hh b/src/runtime.hh new file mode 100644 index 0000000000..d0bb866831 --- /dev/null +++ b/src/runtime.hh @@ -0,0 +1,4 @@ +#ifndef SOCKET_RUNTIME_H +#define SOCKET_RUNTIME_H +#include "runtime/runtime.hh" +#endif diff --git a/src/runtime/bluetooth.hh b/src/runtime/bluetooth.hh new file mode 100644 index 0000000000..703c5cd279 --- /dev/null +++ b/src/runtime/bluetooth.hh @@ -0,0 +1,93 @@ +#ifndef SOCKET_RUNTIME_RUNTIME_BLUETOOTH_H +#define SOCKET_RUNTIME_RUNTIME_BLUETOOTH_H + +#include "ipc.hh" +#include "json.hh" +#include "platform.hh" +#include "queued_response.hh" + +#if SOCKET_RUNTIME_PLATFORM_APPLE +@interface SSCBluetoothController : NSObject< + CBCentralManagerDelegate, + CBPeripheralManagerDelegate, + CBPeripheralDelegate +> +@property (strong, nonatomic) CBCentralManager* centralManager; +@property (strong, nonatomic) CBPeripheralManager* peripheralManager; +@property (strong, nonatomic) CBPeripheral* bluetoothPeripheral; +@property (strong, nonatomic) NSMutableArray* peripherals; +@property (strong, nonatomic) NSMutableDictionary* services; +@property (strong, nonatomic) NSMutableDictionary* characteristics; +@property (strong, nonatomic) NSMutableDictionary* serviceMap; +- (void) startAdvertising; +- (void) startScanning; +- (id) init; +@end +#endif + +namespace ssc::runtime::bluetooth { + class Bluetooth { + public: + using SendHandler = Function; + using EmitHandler = Function; + using Callback = Function; + using CharacteristicID = String; + using ServiceID = String; + + #if SOCKET_RUNTIME_PLATFORM_APPLE + SSCBluetoothController* controller = nullptr; + #endif + + SendHandler sendHandler; + EmitHandler emitHandler; + + Bluetooth (); + ~Bluetooth (); + + bool send (const ipc::Message::Seq&, JSON::Any, QueuedResponse); + bool send (const ipc::Message::Seq&, JSON::Any json); + bool emit (const ipc::Message::Seq&, JSON::Any json); + void startScanning (); + void publishCharacteristic ( + const ipc::Message::Seq&, + const char*, + size_t, + const ServiceID&, + const CharacteristicID&, + const Callback + ); + + void publishCharacteristic ( + const char*, + size_t, + const ServiceID&, + const CharacteristicID&, + const Callback + ); + + void subscribeCharacteristic ( + const ipc::Message::Seq&, + const ServiceID&, + const CharacteristicID&, + const Callback + ); + + void subscribeCharacteristic ( + const ServiceID&, + const CharacteristicID&, + const Callback + ); + + void startService ( + const ipc::Message::Seq&, + const ServiceID&, + const Callback + ); + + void startService ( + const ServiceID&, + const Callback + ); + }; +} +#endif diff --git a/src/core/bluetooth.cc b/src/runtime/bluetooth/bluetooth.cc similarity index 88% rename from src/core/bluetooth.cc rename to src/runtime/bluetooth/bluetooth.cc index e4a55a2038..07b2e66684 100644 --- a/src/core/bluetooth.cc +++ b/src/runtime/bluetooth/bluetooth.cc @@ -1,8 +1,12 @@ -#include "core.hh" -#include "bluetooth.hh" -#include "../ipc/ipc.hh" +#include "../ipc.hh" +#include "../http.hh" +#include "../json.hh" +#include "../debug.hh" +#include "../queued_response.hh" -using namespace SSC; +#include "../bluetooth.hh" + +using namespace ssc::runtime; #if SOCKET_RUNTIME_PLATFORM_APPLE @interface SSCBluetoothController () @@ -121,7 +125,7 @@ using namespace SSC; }} }; - auto result = SSC::IPC::Result(json); + auto result = ipc::Result(json); self.bluetooth->emit("bluetooth", result.json()); } @@ -140,7 +144,7 @@ using namespace SSC; }} }; - auto result = SSC::IPC::Result(json); + auto result = ipc::Result(json); self.bluetooth->emit("bluetooth", result.json()); return; } @@ -272,7 +276,7 @@ using namespace SSC; }} }; - auto result = SSC::IPC::Result(json); + auto result = ipc::Result(json); self.bluetooth->emit("bluetooth", result.json()); [peripheral discoverServices: uuids]; @@ -325,7 +329,7 @@ using namespace SSC; }} }; - auto result = SSC::IPC::Result(json); + auto result = ipc::Result(json); self.bluetooth->emit("bluetooth", result.json()); } @@ -363,19 +367,19 @@ using namespace SSC; auto bytes = (char*) characteristic.value.bytes; auto length = (int) characteristic.value.length; - auto headers = Headers {{ + auto headers = http::Headers {{ {"content-type", "application/octet-stream"}, {"content-length", length} }}; - Post post = {0}; - post.id = rand64(); - post.headers = headers.str(); + QueuedResponse queuedResponse = {0}; + queuedResponse.id = rand64(); + queuedResponse.headers = headers; if (bytes != nullptr && length > 0) { - post.body = std::make_shared(length); - post.length = length; - memcpy(post.body.get(), bytes, length); + queuedResponse.body = std::make_shared(length); + queuedResponse.length = length; + memcpy(queuedResponse.body.get(), bytes, length); } auto json = JSON::Object::Entries { @@ -389,8 +393,8 @@ using namespace SSC; }} }; - auto result = SSC::IPC::Result(json); - self.bluetooth->send(result.seq, result.json(), post); + auto result = ipc::Result(json); + self.bluetooth->send(result.seq, result.json(), queuedResponse); } - (void) peripheral: (CBPeripheral*) peripheral @@ -433,12 +437,11 @@ using namespace SSC; @end #endif -namespace SSC { - Bluetooth::Bluetooth () { - } +namespace ssc::runtime::bluetooth { + Bluetooth::Bluetooth () {} Bluetooth::~Bluetooth () { - #if defined(__APPLE__) + #if SOCKET_RUNTIME_PLATFORM_APPLE if (this->controller != nullptr) { #if !__has_feature(objc_arc) [this->controller release]; @@ -447,28 +450,28 @@ namespace SSC { #endif } - bool Bluetooth::send (const String& seq, JSON::Any json, Post post) { - if (this->sendFunction != nullptr) { - this->sendFunction(seq, json, post); + bool Bluetooth::send (const String& seq, JSON::Any json, QueuedResponse queuedResponse) { + if (this->sendHandler != nullptr) { + this->sendHandler(seq, json, queuedResponse); return true; } return false; } bool Bluetooth::send (const String& seq, JSON::Any json) { - return this->send(seq, json, Post{}); + return this->send(seq, json, QueuedResponse{}); } bool Bluetooth::emit (const String& seq, JSON::Any json) { - if (this->emitFunction != nullptr) { - this->emitFunction(seq, json); + if (this->emitHandler != nullptr) { + this->emitHandler(seq, json); return true; } return false; } void Bluetooth::startScanning () { - #if defined(__APPLE__) + #if SOCKET_RUNTIME_PLATFORM_APPLE if (this->controller == nullptr) { this->controller = [SSCBluetoothController new]; } @@ -481,14 +484,24 @@ namespace SSC { } void Bluetooth::publishCharacteristic ( - const String &seq, - char *bytes, + const char *bytes, + size_t size, + const ServiceID& serviceId, + const CharacteristicID& characteristicId, + const Callback callback + ) { + this->publishCharacteristic("-1", bytes, size, serviceId, characteristicId, callback); + } + + void Bluetooth::publishCharacteristic ( + const ipc::Message::Seq& seq, + const char *bytes, size_t size, - const String &serviceId, - const String &characteristicId, - Callback callback + const ServiceID& serviceId, + const CharacteristicID& characteristicId, + const Callback callback ) { - #if defined(__APPLE__) + #if SOCKET_RUNTIME_PLATFORM_APPLE if (serviceId.size() != 36) { callback(seq, JSON::Object::Entries { {"source", "bluetooth.publish"}, @@ -601,10 +614,18 @@ namespace SSC { } void Bluetooth::subscribeCharacteristic ( - const String &seq, - const String &serviceId, - const String &characteristicId, - Callback callback + const ServiceID& serviceId, + const CharacteristicID& characteristicId, + const Callback callback + ) { + this->subscribeCharacteristic("-1", serviceId, characteristicId, callback); + } + + void Bluetooth::subscribeCharacteristic ( + const ipc::Message::Seq& seq, + const ServiceID& serviceId, + const CharacteristicID& characteristicId, + const Callback callback ) { auto json = JSON::Object::Entries { {"err", JSON::Object::Entries { @@ -613,7 +634,7 @@ namespace SSC { }} }; - #if defined(__APPLE__) + #if SOCKET_RUNTIME_PLATFORM_APPLE auto ssid = [NSString stringWithUTF8String: serviceId.c_str()]; auto scid = [NSString stringWithUTF8String: characteristicId.c_str()]; @@ -666,9 +687,16 @@ namespace SSC { } void Bluetooth::startService ( - const String &seq, - const String &serviceId, - Callback callback + const ServiceID& serviceId, + const Callback callback + ) { + this->startService("-1", serviceId, callback); + } + + void Bluetooth::startService ( + const ipc::Message::Seq& seq, + const ServiceID& serviceId, + const Callback callback ) { auto json = JSON::Object::Entries { {"err", JSON::Object::Entries { @@ -677,7 +705,7 @@ namespace SSC { }} }; - #if defined(__APPLE__) + #if SOCKET_RUNTIME_PLATFORM_APPLE if (this->controller != nullptr) { [this->controller startScanning]; json = JSON::Object::Entries { diff --git a/src/runtime/bridge.hh b/src/runtime/bridge.hh new file mode 100644 index 0000000000..3d55f76383 --- /dev/null +++ b/src/runtime/bridge.hh @@ -0,0 +1,178 @@ +#ifndef SOCKET_RUNTIME_BRIDGE_H +#define SOCKET_RUNTIME_BRIDGE_H + +#include "ipc.hh" +#include "json.hh" +#include "window.hh" +#include "context.hh" +#include "webview.hh" +#include "queued_response.hh" + +#include "core/services.hh" + +namespace ssc::runtime::bridge { + using Client = ipc::Client; + + /** + * The `Bridge` class represents a bi-directional interface between the + * runtime services and a window. + */ + class Bridge : public window::IBridge { + public: + using ID = uint64_t; + + struct Options { + Client client; + context::RuntimeContext& context; + context::Dispatcher& dispatcher; + webview::Preload::Options preload; + Map userConfig; + }; + + const core::services::NetworkStatus::Observer networkStatusObserver; + const core::services::Geolocation::PermissionChangeObserver geolocationPermissionChangeObserver; + const core::services::Notifications::PermissionChangeObserver notificationsPermissionChangeObserver; + const core::services::Notifications::NotificationResponseObserver notificationResponseObserver; + const core::services::Notifications::NotificationPresentedObserver notificationPresentedObserver; + + using window::IBridge::IBridge; + Bridge (const Options&); + ~Bridge (); + + void init () override; + + // `ipc::IBridge` + bool active () const override; + bool emit (const String&, const String& = "") override; + bool emit (const String&, const JSON::Any& = {}) override; + bool send (const ipc::Message::Seq&, const String&, const QueuedResponse& = {}) override; + bool send (const ipc::Message::Seq&, const JSON::Any&, const QueuedResponse& = {}) override; + bool route (const String&, SharedPointer, size_t) override; + bool route (const String&, SharedPointer, size_t, const ipc::Router::ResultCallback) override; + + // `window::IBridge` + void configureWebView (webview::WebView* webview) override; + void configureSchemeHandlers (const webview::SchemeHandlers::Configuration&) override; + void configureNavigatorMounts () override; + bool evaluateJavaScript (const String&) override; + bool dispatch (const context::DispatchCallback) override; + // `webview::IBridge` + bool navigate (const String& url) override; + }; + + class Manager { + public: + Vector> entries; + context::RuntimeContext& context; + Mutex mutex; + + Manager (context::RuntimeContext&); + Manager () = delete; + Manager (const Manager&) = delete; + Manager (Manager&&) = delete; + virtual ~Manager(); + + Manager& operator = (const Manager&) = delete; + Manager& operator = (Manager&&) = delete; + + SharedPointer get (int); + bool has (int) const; + bool remove (int); + }; + + /* + class Bridge { + public: + using EvaluateJavaScriptFunction = Function; + using NavigateFunction = Function; + using DispatchCallback = Function; + using DispatchFunction = Function; + + struct Options : public SSC::Options { + Map userConfig = {}; + Preload::Options preload; + uint64_t id = 0; + int index = 0; + Options ( + int index, + const Map userConfig = {}, + const Preload::Options preload = {}, + uint64_t id = 0 + ); + }; + + static Vector getInstances(); + + const CoreNetworkStatus::Observer networkStatusObserver; + const CoreGeolocation::PermissionChangeObserver geolocationPermissionChangeObserver; + const CoreNotifications::PermissionChangeObserver notificationsPermissionChangeObserver; + const CoreNotifications::NotificationResponseObserver notificationResponseObserver; + const CoreNotifications::NotificationPresentedObserver notificationPresentedObserver; + + EvaluateJavaScriptFunction evaluateJavaScriptFunction = nullptr; + NavigateFunction navigateFunction = nullptr; + DispatchFunction dispatchFunction = nullptr; + + Client client = {}; + Map userConfig; + Navigator navigator; + Router router; + SchemeHandlers schemeHandlers; + + uint64_t id = 0; + int index = 0; + + #if SOCKET_RUNTIME_PLATFORM_ANDROID + bool isAndroidEmulator = false; + #elif SOCKET_RUNTIME_PLATFORM_LINUX && !SOCKET_RUNTIME_DESKTOP_EXTENSION + WebKitWebContext* webContext = nullptr; + #endif + + Bridge (SharedPointercore, const Options& options); + Bridge () = delete; + Bridge (const Bridge&) = delete; + Bridge (Bridge&&) = delete; + virtual ~Bridge (); + + Bridge& operator = (const Bridge&) = delete; + Bridge& operator = (Bridge&&) = delete; + + void init (); + void configureWebView (WebView* webview); + void configureSchemeHandlers ( + const SchemeHandlers::Configuration& configuration + ); + void configureNavigatorMounts (); + + bool route ( + const String& uri, + SharedPointer bytes, + size_t size + ); + + bool route ( + const String& uri, + SharedPointer bytes, + size_t size, + Router::ResultCallback + ); + + bool evaluateJavaScript (const String& source); + bool dispatch (const DispatchCallback callback); + bool navigate (const String& url); + bool emit (const String& name, const String& data = ""); + bool emit (const String& name, const JSON::Any& json = {}); + bool send ( + const Message::Seq& seq, + const String& data, + const QueuedResponse& post = {} + ); + bool send ( + const Message::Seq& seq, + const JSON::Any& json, + const QueuedResponse& post = {} + ); + }; + */ +} +#endif diff --git a/src/ipc/bridge.cc b/src/runtime/bridge/bridge.cc similarity index 64% rename from src/ipc/bridge.cc rename to src/runtime/bridge/bridge.cc index 1c4786d41c..ff7e5f605a 100644 --- a/src/ipc/bridge.cc +++ b/src/runtime/bridge/bridge.cc @@ -1,17 +1,38 @@ -#include "../serviceworker/protocols.hh" -#include "../extension/extension.hh" -#include "../window/window.hh" -#include "../core/version.hh" -#include "../app/app.hh" -#include "ipc.hh" - -extern const SSC::Map SSC::getUserConfig (); -extern bool SSC::isDebugEnabled (); - -namespace SSC::IPC { - static Vector instances; - static Mutex mutex; - +#include "../filesystem.hh" +#include "../javascript.hh" +#include "../platform.hh" +#include "../runtime.hh" +#include "../version.hh" +#include "../webview.hh" +#include "../string.hh" +#include "../window.hh" +#include "../cwd.hh" +#include "../env.hh" +#include "../ipc.hh" +#include "../url.hh" + +#include "../bridge.hh" + +//extern const SSC::Map SSC::getUserConfig (); +//extern bool SSC::isDebugEnabled (); + +using ssc::runtime::javascript::getResolveToRenderProcessJavaScript; +using ssc::runtime::javascript::getEmitToRenderProcessJavaScript; +using ssc::runtime::url::encodeURIComponent; +using ssc::runtime::webview::SchemeHandlers; + +using ssc::runtime::config::isDebugEnabled; +using ssc::runtime::config::getUserConfig; + +using ssc::runtime::string::parseStringList; +using ssc::runtime::string::toLowerCase; +using ssc::runtime::string::replace; +using ssc::runtime::string::split; +using ssc::runtime::string::tmpl; +using ssc::runtime::string::trim; +using ssc::runtime::string::join; + +namespace ssc::runtime::bridge { // The `ESM_IMPORT_PROXY_TEMPLATE` is used to provide an ESM module as // a proxy to a canonical URL for a module import. static constexpr auto ESM_IMPORT_PROXY_TEMPLATE_WITH_DEFAULT_EXPORT = R"S( @@ -78,163 +99,50 @@ export * from '{{url}}' "worker_threads" }; -#if SOCKET_RUNTIME_PLATFORM_DESKTOP - static FileSystemWatcher* developerResourcesFileSystemWatcher = nullptr; - static void initializeDeveloperResourcesFileSystemWatcher (SharedPointer core) { - auto defaultUserConfig = SSC::getUserConfig(); - if ( - developerResourcesFileSystemWatcher == nullptr && - isDebugEnabled() && - defaultUserConfig["webview_watch"] == "true" - ) { - developerResourcesFileSystemWatcher = new FileSystemWatcher(getcwd()); - developerResourcesFileSystemWatcher->core = core.get(); - developerResourcesFileSystemWatcher->start([=]( - const auto& path, - const auto& events, - const auto& context - ) mutable { - Lock lock(SSC::IPC::mutex); - - static const auto cwd = getcwd(); - const auto relativePath = fs::relative(path, cwd).string(); - const auto json = JSON::Object::Entries {{"path", relativePath}}; - const auto result = SSC::IPC::Result(json); - - for (auto& bridge : instances) { - auto userConfig = bridge->userConfig; - if ( - !platform.ios && - !platform.android && - userConfig["webview_watch"] == "true" && - bridge->userConfig["webview_service_worker_mode"] != "hybrid" && - (!userConfig.contains("webview_watch_reload") || userConfig.at("webview_watch_reload") != "false") - ) { - // check if changed path was a service worker, if so unregister it so it can be reloaded - for (const auto& entry : App::sharedApplication()->serviceWorkerContainer.registrations) { - const auto& registration = entry.second; - #if SOCKET_RUNTIME_PLATFORM_ANDROID - auto scriptURL = String("https://"); - #else - auto scriptURL = String("socket://"); - #endif - - scriptURL += userConfig["meta_bundle_identifier"]; - - if (!relativePath.starts_with("/")) { - scriptURL += "/"; - } - - scriptURL += relativePath; - if (registration.scriptURL == scriptURL) { - // 1. unregister service worker - // 2. re-register service worker - // 3. wait for it to be registered - // 4. emit 'filedidchange' event - bridge->navigator.serviceWorker.unregisterServiceWorker(entry.first); - bridge->core->setTimeout(8, [bridge, result, ®istration] () { - bridge->core->setInterval(8, [bridge, result, ®istration] (auto cancel) { - if (registration.state == ServiceWorkerContainer::Registration::State::Activated) { - cancel(); - - uint64_t timeout = 500; - if (bridge->userConfig["webview_watch_service_worker_reload_timeout"].size() > 0) { - try { - timeout = std::stoull(bridge->userConfig["webview_watch_service_worker_reload_timeout"]); - } catch (...) {} - } - - bridge->core->setTimeout(timeout, [bridge, result] () { - bridge->emit("filedidchange", result.json().str()); - }); - } - }); - - bridge->navigator.serviceWorker.registerServiceWorker(registration.options); - }); - return; - } - } - } - - bridge->emit("filedidchange", result.json().str()); - } - }); - } - } -#endif - Bridge::Options::Options ( - int index, - const Map& userConfig, - const Preload::Options& preload, - uint64_t id - ) : userConfig(userConfig), - preload(preload), - index(index), - id(id) - {} - Bridge::Bridge ( - SharedPointer core, const Options& options - ) : core(core), - userConfig(options.userConfig), - router(this), - navigator(this), - schemeHandlers(this) + ) : window::IBridge(options.dispatcher, options.context, options.client, options.userConfig) { - this->id = options.id > 0 ? options.id : rand64(); // '-1' may mean the bridge is running as a WebKit web process extension - if (options.index >= 0) { - const auto windowClientConfigKey = String("window.") + std::to_string(options.index) + ".client"; + if (options.client.index >= 0) { + const auto windowClientConfigKey = String("window.") + std::to_string(options.client.index) + ".client"; // handle user defined window client id if (this->userConfig[windowClientConfigKey + ".id"].size() > 0) { try { - this->id = std::stoull(this->userConfig[windowClientConfigKey + ".id"]); + this->client.id = std::stoull(this->userConfig[windowClientConfigKey + ".id"]); } catch (const Exception& e) { - debug("Invalid window client ID given in '[window.%d.client] id'", options.index); + debug("Invalid window client ID given in '[window.%d.client] id'", options.client.index); } } } - this->client.id = this->id; - #if SOCKET_RUNTIME_PLATFORM_ANDROID - this->isAndroidEmulator = App::sharedApplication()->isAndroidEmulator; - #endif - - this->bluetooth.sendFunction = [this]( - const String& seq, + this->bluetooth.sendHandler = [this]( + const ipc::Message::Seq& seq, const JSON::Any value, - const SSC::Post post + const QueuedResponse queuedResponse ) { - this->send(seq, value.str(), post); + this->send(seq, value.str(), queuedResponse); }; - this->bluetooth.emitFunction = [this]( - const String& seq, + this->bluetooth.emitHandler = [this]( + const ipc::Message::Seq& seq, const JSON::Any value ) { this->emit(seq, value.str()); }; - this->dispatchFunction = [] (auto callback) { - #if SOCKET_RUNTIME_PLATFORM_ANDROID - callback(); - #else - App::sharedApplication()->dispatch(callback); - #endif - }; + auto& runtime = static_cast(this->context); - core->networkStatus.addObserver(this->networkStatusObserver, [this](auto json) { + runtime.services.networkStatus.addObserver(this->networkStatusObserver, [this](auto json) { if (json.has("name")) { this->emit(json["name"].str(), json.str()); } }); - core->networkStatus.start(); + runtime.services.networkStatus.start(); - core->geolocation.addPermissionChangeObserver(this->geolocationPermissionChangeObserver, [this] (auto json) { + runtime.services.geolocation.addPermissionChangeObserver(this->geolocationPermissionChangeObserver, [this] (auto json) { JSON::Object event = JSON::Object::Entries { {"name", "geolocation"}, {"state", json["state"]} @@ -246,7 +154,7 @@ export * from '{{url}}' // below are not needed as those events already occur in the webview // we are patching for the other platforms #if !SOCKET_RUNTIME_PLATFORM_LINUX - core->notifications.addPermissionChangeObserver(this->notificationsPermissionChangeObserver, [this](auto json) { + runtime.services.notifications.addPermissionChangeObserver(this->notificationsPermissionChangeObserver, [this](auto json) { JSON::Object event = JSON::Object::Entries { {"name", "notifications"}, {"state", json["state"]} @@ -255,47 +163,25 @@ export * from '{{url}}' }); if (userConfig["permissions_allow_notifications"] != "false") { - core->notifications.addNotificationResponseObserver(this->notificationResponseObserver, [this](auto json) { + runtime.services.notifications.addNotificationResponseObserver(this->notificationResponseObserver, [this](auto json) { this->emit("notificationresponse", json.str()); }); - core->notifications.addNotificationPresentedObserver(this->notificationPresentedObserver, [this](auto json) { + runtime.services.notifications.addNotificationPresentedObserver(this->notificationPresentedObserver, [this](auto json) { this->emit("notificationpresented", json.str()); }); } #endif - - Lock lock(SSC::IPC::mutex); - instances.push_back(this); - #if SOCKET_RUNTIME_PLATFORM_DESKTOP - initializeDeveloperResourcesFileSystemWatcher(core); - #endif } Bridge::~Bridge () { + auto& runtime = static_cast(this->context); // remove observers - core->geolocation.removePermissionChangeObserver(this->geolocationPermissionChangeObserver); - core->networkStatus.removeObserver(this->networkStatusObserver); - core->notifications.removePermissionChangeObserver(this->notificationsPermissionChangeObserver); - core->notifications.removeNotificationResponseObserver(this->notificationResponseObserver); - core->notifications.removeNotificationPresentedObserver(this->notificationPresentedObserver); - - do { - Lock lock(SSC::IPC::mutex); - const auto cursor = std::find(instances.begin(), instances.end(), this); - if (cursor != instances.end()) { - instances.erase(cursor); - } - - #if SOCKET_RUNTIME_PLATFORM_DESKTOP - if (instances.size() == 0) { - if (developerResourcesFileSystemWatcher) { - developerResourcesFileSystemWatcher->stop(); - delete developerResourcesFileSystemWatcher; - } - } - #endif - } while (0); + runtime.services.geolocation.removePermissionChangeObserver(this->geolocationPermissionChangeObserver); + runtime.services.networkStatus.removeObserver(this->networkStatusObserver); + runtime.services.notifications.removePermissionChangeObserver(this->notificationsPermissionChangeObserver); + runtime.services.notifications.removeNotificationResponseObserver(this->notificationResponseObserver); + runtime.services.notifications.removeNotificationPresentedObserver(this->notificationPresentedObserver); } void Bridge::init () { @@ -304,43 +190,33 @@ export * from '{{url}}' this->schemeHandlers.init(); } - void Bridge::configureWebView (WebView* webview) { + void Bridge::configureWebView (webview::WebView* webview) { this->navigator.configureWebView(webview); } bool Bridge::evaluateJavaScript (const String& source) { - if (this->core->isShuttingDown) { - return false; - } - - if (this->evaluateJavaScriptFunction != nullptr) { - this->evaluateJavaScriptFunction(source); + if (this->evaluateJavaScriptHandler != nullptr) { + this->evaluateJavaScriptHandler(source); return true; } return false; } - bool Bridge::dispatch (const DispatchCallback& callback) { - if (!this->core || this->core->isShuttingDown) { - return false; - } - - if (this->dispatchFunction != nullptr) { - this->dispatchFunction(callback); + bool Bridge::dispatch (const context::DispatchCallback callback) { + #if SOCKET_RUNTIME_PLATFORM_ANDROID + callback(); return true; - } + #else + return static_cast(this->context).dispatch(callback); + #endif return false; } bool Bridge::navigate (const String& url) { - if (!this->core || this->core->isShuttingDown) { - return false; - } - - if (this->navigateFunction != nullptr) { - this->navigateFunction(url); + if (this->navigateHandler != nullptr) { + this->navigateHandler(url); return true; } @@ -355,7 +231,7 @@ export * from '{{url}}' const String& uri, SharedPointer bytes, size_t size, - Router::ResultCallback callback + ipc::Router::ResultCallback callback ) { if (callback != nullptr) { return this->router.invoke(uri, bytes, size, callback); @@ -365,16 +241,12 @@ export * from '{{url}}' } bool Bridge::send ( - const Message::Seq& seq, + const ipc::Message::Seq& seq, const String& data, - const Post& post + const QueuedResponse& queuedResponse ) { - if (this->core->isShuttingDown) { - return false; - } - - if (post.body != nullptr || seq == "-1") { - const auto script = this->core->createPost(seq, data, post); + if (queuedResponse.body != nullptr || seq == "-1") { + const auto script = this->context.createQueuedResponse(seq, data, queuedResponse); return this->evaluateJavaScript(script); } @@ -388,15 +260,11 @@ export * from '{{url}}' return this->evaluateJavaScript(script); } - bool Bridge::send (const Message::Seq& seq, const JSON::Any& json, const Post& post) { - return this->send(seq, json.str(), post); + bool Bridge::send (const ipc::Message::Seq& seq, const JSON::Any& json, const QueuedResponse& queuedResponse) { + return this->send(seq, json.str(), queuedResponse); } bool Bridge::emit (const String& name, const String& data) { - if (this->core->isShuttingDown) { - return false; - } - const auto value = encodeURIComponent(data); const auto script = getEmitToRenderProcessJavaScript(name, value); return this->evaluateJavaScript(script); @@ -416,7 +284,7 @@ export * from '{{url}}' auto callbacks, auto callback ) { - auto message = Message(request->url(), true); + auto message = ipc::Message(request->url()); if (request->method == "OPTIONS") { auto response = SchemeHandlers::Response(request, 204); @@ -424,7 +292,7 @@ export * from '{{url}}' } message.isHTTP = true; - message.cancel = std::make_shared(); + message.cancel = std::make_shared(); callbacks->cancel = [message] () { if (message.cancel->handler != nullptr) { @@ -434,7 +302,7 @@ export * from '{{url}}' const auto size = request->body.size; const auto bytes = request->body.bytes; - const auto invoked = this->router.invoke(message.str(), request->body.bytes, size, [=](Result result) { + const auto invoked = this->router.invoke(message.str(), request->body.bytes, size, [=](ipc::Result result) { if (!request->isActive()) { return; } @@ -444,10 +312,10 @@ export * from '{{url}}' response.setHeaders(result.headers); // handle event source streams - if (result.post.eventStream != nullptr) { + if (result.queuedResponse.eventStreamCallback != nullptr) { response.setHeader("content-type", "text/event-stream"); response.setHeader("cache-control", "no-store"); - *result.post.eventStream = [request, response, message, callback]( + *result.queuedResponse.eventStreamCallback = [request, response, message, callback]( const char* name, const char* data, bool finished @@ -477,9 +345,9 @@ export * from '{{url}}' } // handle chunk streams - if (result.post.chunkStream != nullptr) { + if (result.queuedResponse.chunkStreamCallback != nullptr) { response.setHeader("transfer-encoding", "chunked"); - *result.post.chunkStream = [request, response, message, callback]( + *result.queuedResponse.chunkStreamCallback = [request, response, message, callback]( const char* chunk, size_t size, bool finished @@ -503,8 +371,8 @@ export * from '{{url}}' return; } - if (result.post.body != nullptr) { - response.write(result.post.length, result.post.body); + if (result.queuedResponse.body != nullptr) { + response.write(result.queuedResponse.length, result.queuedResponse.body); } else { response.write(result.json()); } @@ -532,10 +400,13 @@ export * from '{{url}}' auto callbacks, auto callback ) { + auto globalConfig = getUserConfig(); auto userConfig = this->userConfig; auto bundleIdentifier = userConfig["meta_bundle_identifier"]; - auto app = App::sharedApplication(); - auto window = app->windowManager.getWindowForBridge(bridge); + auto globalBundleIdentifier = globalConfig["meta_bundle_identifier"]; + auto window = this->context.getRuntime()->windowManager.getWindowForBridge( + reinterpret_cast(bridge) + ); // if there was no window, then this is a bad request as scheme // handlers should only be handled directly in a window with @@ -555,7 +426,9 @@ export * from '{{url}}' } // the location of static application resources - const auto applicationResources = FileResource::getResourcesPath().string(); + const auto applicationResources = window->options.resourcesDirectory.size() > 0 + ? fs::absolute(window->options.resourcesDirectory).string() + : filesystem::Resource::getResourcesPath().string(); // default response is 404 auto response = SchemeHandlers::Response(request, 404); @@ -566,7 +439,157 @@ export * from '{{url}}' String contentLocation; // application resource or service worker request at `socket:///*` - if (toLowerCase(request->hostname) == toLowerCase(bundleIdentifier)) { + if (request->hostname.size() > 0) { + if ( + request->hostname != globalBundleIdentifier && + window->options.shouldPreferServiceWorker && + this->navigator.serviceWorker.registrations.size() > 0 + ) { + auto fetch = ServiceWorkerContainer::FetchRequest { + request->method, + request->scheme, + request->hostname, + request->pathname, + request->query, + request->headers, + ServiceWorkerContainer::FetchBody { request->body.size, request->body.bytes }, + request->client + }; + + if (!fetch.headers.has("origin")) { + fetch.headers.set("origin", this->navigator.location.origin); + } + + const auto fetched = this->navigator.serviceWorker.fetch(fetch, [ + this, + applicationResources, + contentLocation, + resourcePath, + userConfig, + callback, + response, + request + ] (auto res) mutable { + if (!request->isActive()) { + return; + } + + if (res.statusCode == 0) { + response.fail("ServiceWorker request failed"); + } else if (res.statusCode != 404) { + response.writeHead(res.statusCode, res.headers); + response.write(res.body.size, res.body.bytes); + } else { + const auto resolved = this->navigator.location.resolve(request->pathname, applicationResources); + + if (resolved.redirect) { + if (request->method == "GET") { + auto location = resolved.pathname; + if (request->query.size() > 0) { + location += "?" + request->query; + } + + if (request->fragment.size() > 0) { + location += "#" + request->fragment; + } + + response.redirect(location); + return callback(response); + } + } else if (resolved.isResource()) { + resourcePath = applicationResources + resolved.pathname; + } else if (resolved.isMount()) { + resourcePath = resolved.mount.filename; + } else if (request->pathname == "" || request->pathname == "/") { + if (userConfig.contains("webview_default_index")) { + resourcePath = userConfig["webview_default_index"]; + if (resourcePath.starts_with("./")) { + resourcePath = applicationResources + resourcePath.substr(1); + } else if (resourcePath.starts_with("/")) { + resourcePath = applicationResources + resourcePath; + } else { + resourcePath = applicationResources + + "/" + resourcePath; + } + } + } + + if (resourcePath.size() == 0 && resolved.pathname.size() > 0) { + resourcePath = applicationResources + resolved.pathname; + } + + // handle HEAD and GET requests for a file resource + if (resourcePath.size() > 0) { + if (resourcePath.starts_with(applicationResources)) { + contentLocation = resourcePath.substr(applicationResources.size(), resourcePath.size()); + } + + auto resource = filesystem::Resource(resourcePath); + + if (!resource.exists()) { + response.writeHead(404); + } else { + if (contentLocation.size() > 0) { + response.setHeader("content-location", contentLocation); + } + + if (request->method == "OPTIONS") { + response.writeHead(204); + } + + if (request->method == "HEAD") { + const auto contentType = resource.mimeType(); + const auto contentLength = resource.size(); + + if (contentType.size() > 0) { + response.setHeader("content-type", contentType); + } + + if (contentLength > 0) { + response.setHeader("content-length", contentLength); + } + + response.writeHead(200); + } + + if (request->method == "GET") { + if (resource.mimeType() != "text/html") { + response.setHeader("cache-control", "public"); + response.send(resource); + } else { + const auto html = request->headers["runtime-preload-injection"] == "disabled" + ? resource.str() + : this->client.preload.insertIntoHTML(resource.str(), { + .protocolHandlerSchemes = this->navigator.serviceWorker.protocols.getSchemes() + }); + + response.setHeader("content-type", "text/html"); + response.setHeader("content-length", html.size()); + response.setHeader("cache-control", "public"); + response.writeHead(200); + response.write(html); + } + } + } + + return callback(response); + } + } + + callback(response); + }); + + if (fetched) { + // FIXME(@jwerle): revisit timeout + //this->core->setTimeout(32000, [request] () mutable { + //if (request->isActive()) { + //auto response = SchemeHandlers::Response(request, 408); + //response.fail("ServiceWorker request timed out."); + //} + //}); + return; + } + } + const auto resolved = this->navigator.location.resolve(request->pathname, applicationResources); if (resolved.redirect) { @@ -610,7 +633,7 @@ export * from '{{url}}' contentLocation = resourcePath.substr(applicationResources.size(), resourcePath.size()); } - auto resource = FileResource(resourcePath); + auto resource = filesystem::Resource(resourcePath); if (!resource.exists()) { response.writeHead(404); @@ -662,7 +685,7 @@ export * from '{{url}}' } if (this->navigator.serviceWorker.registrations.size() > 0) { - const auto fetch = ServiceWorkerContainer::FetchRequest { + auto fetch = ServiceWorkerContainer::FetchRequest { request->method, request->scheme, request->hostname, @@ -673,6 +696,10 @@ export * from '{{url}}' request->client }; + if (!fetch.headers.has("origin")) { + fetch.headers.set("origin", this->navigator.location.origin); + } + const auto fetched = this->navigator.serviceWorker.fetch(fetch, [request, callback, response] (auto res) mutable { if (!request->isActive()) { return; @@ -726,7 +753,7 @@ export * from '{{url}}' resourcePath = applicationResources + "/socket" + pathname; contentLocation = "/socket" + pathname; - auto resource = FileResource(resourcePath, { .cache = true }); + auto resource = filesystem::Resource(resourcePath, { .cache = true }); if (resource.exists()) { const auto url = ( @@ -735,7 +762,7 @@ export * from '{{url}}' #else "socket://" + #endif - toLowerCase(bundleIdentifier) + + toLowerCase(globalBundleIdentifier) + contentLocation + (request->query.size() > 0 ? "?" + request->query : "") ); @@ -744,13 +771,13 @@ export * from '{{url}}' String(resource.read()).find("export default") != String::npos ? ESM_IMPORT_PROXY_TEMPLATE_WITH_DEFAULT_EXPORT : ESM_IMPORT_PROXY_TEMPLATE_WITHOUT_DEFAULT_EXPORT, - Map { + Map { {"url", url}, - {"commit", VERSION_HASH_STRING}, + {"commit", version::VERSION_HASH_STRING}, {"protocol", "socket"}, {"pathname", pathname}, {"specifier", specifier}, - {"bundle_identifier", toLowerCase(bundleIdentifier)} + {"bundle_identifier", toLowerCase(globalBundleIdentifier)} } ); @@ -790,10 +817,10 @@ export * from '{{url}}' return; } - auto userConfig = this->userConfig; - auto bundleIdentifier = userConfig["meta_bundle_identifier"]; + auto userConfig = getUserConfig(); + const auto bundleIdentifier = userConfig["meta_bundle_identifier"]; // the location of static application resources - const auto applicationResources = FileResource::getResourcesPath().string(); + const auto applicationResources = filesystem::Resource::getResourcesPath().string(); // default response is 404 auto response = SchemeHandlers::Response(request, 404); @@ -830,7 +857,7 @@ export * from '{{url}}' contentLocation = "/socket" + pathname; resourcePath = applicationResources + contentLocation; - auto resource = FileResource(resourcePath, { .cache = true }); + auto resource = filesystem::Resource(resourcePath, { .cache = true }); if (!resource.exists()) { if (!pathname.ends_with(".js")) { @@ -848,7 +875,7 @@ export * from '{{url}}' resourcePath = applicationResources + contentLocation; } - resource = FileResource(resourcePath, { .cache = true }); + resource = filesystem::Resource(resourcePath, { .cache = true }); } if (resource.exists()) { @@ -866,9 +893,9 @@ export * from '{{url}}' String(resource.read()).find("export default") != String::npos ? ESM_IMPORT_PROXY_TEMPLATE_WITH_DEFAULT_EXPORT : ESM_IMPORT_PROXY_TEMPLATE_WITHOUT_DEFAULT_EXPORT, - Map { + Map { {"url", url}, - {"commit", VERSION_HASH_STRING}, + {"commit", version::VERSION_HASH_STRING}, {"protocol", "node"}, {"pathname", pathname}, {"specifier", pathname.substr(1)}, @@ -899,7 +926,7 @@ export * from '{{url}}' callback(response); }); - Map protocolHandlers = { + Map protocolHandlers = { {"npm", "/socket/npm/service-worker.js"} }; @@ -957,13 +984,49 @@ export * from '{{url}}' scriptURL ); - this->navigator.serviceWorker.registerServiceWorker({ - .type = ServiceWorkerContainer::RegistrationOptions::Type::Module, - .scope = scope, - .scriptURL = scriptURL, - .scheme = scheme, - .id = id - }); + auto env = JSON::Object::Entries {}; + for (const auto& entry : this->userConfig) { + if (entry.first.starts_with("env_")) { + env[entry.first.substr(4)] = entry.second; + } else if (entry.first == "build_env") { + const auto keys = parseStringList(entry.second, { ',', ' ' }); + for (const auto& key : keys) { + env[key] = env::get(key); + } + } + } + + if (scheme == "npm") { + this->navigator.serviceWorker.registerServiceWorker({ + .type = ServiceWorkerContainer::RegistrationOptions::Type::Module, + .scope = scope, + .scriptURL = scriptURL, + .scheme = scheme, + .serializedWorkerArgs = "", + .id = id + }); + } else { + this->navigator.serviceWorker.registerServiceWorker({ + .type = ServiceWorkerContainer::RegistrationOptions::Type::Module, + .scope = scope, + .scriptURL = scriptURL, + .scheme = scheme, + .serializedWorkerArgs = encodeURIComponent(JSON::Object(JSON::Object::Entries { + {"index", this->client.index}, + {"argv", JSON::Array {}}, + {"env", env}, + {"debug", isDebugEnabled()}, + {"headless", this->userConfig["build_headless"] == "true"}, + {"config", this->userConfig}, + {"conduit", JSON::Object::Entries { + {"port", this->context.getRuntime()->services.conduit.port}, + {"hostname", this->context.getRuntime()->services.conduit.hostname}, + {"sharedKey", this->context.getRuntime()->services.conduit.sharedKey} + }} + }).str()), + .id = id + }); + } this->schemeHandlers.registerSchemeHandler(scheme, [this]( auto request, @@ -971,8 +1034,9 @@ export * from '{{url}}' auto callbacks, auto callback ) { - auto app = App::sharedApplication(); - auto window = app->windowManager.getWindowForBridge(bridge); + auto window = this->context.getRuntime()->windowManager.getWindowForBridge( + reinterpret_cast(bridge) + ); if (window == nullptr) { auto response = SchemeHandlers::Response(request); @@ -993,10 +1057,12 @@ export * from '{{url}}' auto pathname = request->pathname; if (request->scheme == "npm") { + auto userConfig = getUserConfig(); + const auto bundleIdentifier = userConfig["meta_bundle_identifier"]; if (hostname.size() > 0) { pathname = "/" + hostname; } - hostname = this->userConfig["meta_bundle_identifier"]; + hostname = bundleIdentifier; } const auto scope = this->navigator.serviceWorker.protocols.getServiceWorkerScope(request->scheme); @@ -1005,7 +1071,7 @@ export * from '{{url}}' pathname = scope + pathname; } - const auto fetch = ServiceWorkerContainer::FetchRequest { + auto fetch = ServiceWorkerContainer::FetchRequest { request->method, request->scheme, hostname, @@ -1016,7 +1082,15 @@ export * from '{{url}}' request->client }; - const auto fetched = this->navigator.serviceWorker.fetch(fetch, [request, callback] (auto res) mutable { + if (!fetch.headers.has("origin")) { + fetch.headers.set("origin", this->navigator.location.origin); + } + + auto options = ServiceWorkerContainer::FetchOptions { + .waitForRegistrationToFinish = request->scheme != "npm" + }; + + const auto fetched = this->navigator.serviceWorker.fetch(fetch, options, [request, callback] (auto res) mutable { if (!request->isActive()) { return; } @@ -1113,7 +1187,7 @@ extern "C" { jstring eventString, jstring dataString ) { - using namespace SSC; + using namespace ssc::runtime; auto app = App::sharedApplication(); if (!app) { @@ -1128,8 +1202,8 @@ extern "C" { return false; } - const auto event = Android::StringWrap(env, eventString).str(); - const auto data = Android::StringWrap(env, dataString).str(); + const auto event = android::StringWrap(env, eventString).str(); + const auto data = android::StringWrap(env, dataString).str(); return window->bridge.emit(event, data); } } diff --git a/src/ipc/bridge.kt b/src/runtime/bridge/bridge.kt similarity index 100% rename from src/ipc/bridge.kt rename to src/runtime/bridge/bridge.kt diff --git a/src/runtime/bridge/manager.cc b/src/runtime/bridge/manager.cc new file mode 100644 index 0000000000..e074dbee42 --- /dev/null +++ b/src/runtime/bridge/manager.cc @@ -0,0 +1,32 @@ +#include "../runtime.hh" +#include "../bridge.hh" + +namespace ssc::runtime::bridge { + Manager::Manager (context::RuntimeContext& context) + : context(context) + {} + + SharedPointer Manager::get (int index) { + if (!this->has(index)) { + this->entries[index].reset(new Bridge({ + this->context, + static_cast(this->context).dispatcher + })); + } + + return this->entries[index]; + } + + bool Manager::has (int index) const { + return index < this->entries.size() && this->entries.at(index) != nullptr; + } + + bool Manager::remove (int index) { + if (this->has(index)) { + this->entries.erase(this->entries.begin() + index); + return true; + } + + return false; + } +} diff --git a/src/runtime/bytes.hh b/src/runtime/bytes.hh new file mode 100644 index 0000000000..624058da56 --- /dev/null +++ b/src/runtime/bytes.hh @@ -0,0 +1,156 @@ +#ifndef SOCKET_RUNTIME_BYTES_H +#define SOCKET_RUNTIME_BYTES_H + +#include "platform.hh" + +namespace ssc::runtime::bytes { + template + using ByteArray = Array; + + /** + * Encodes input as a string of hex characters. + * @param input The input string to encode + * @return An encoded string value + */ + String encodeHexString (const String& input); + String encodeHexString (const Vector& input); + + /** + * Decodes input as a string of hex characters to a normal string. + * @param input The input string to encode + * @return An encoded string value + */ + String decodeHexString (const String& input); + + /** + * Converts a `uint64_t` to a array of `uint8_t` values (bytes) + * @param input The `uint64_t` to convert to an array of bytes + * @return An array of `uint8_t` values + */ + const ByteArray<8> toByteArray (const uint64_t input); + const ByteArray<4> toByteArray (const uint32_t input); + const ByteArray<2> toByteArray (const uint16_t input); + + size_t base64_encode_length (size_t inputSize); + size_t base64_decode_length (size_t inputSize); + + size_t base64_encode ( + const unsigned char* input, + size_t inputSize, + char *output, + size_t outputSize + ); + + size_t base64_decode ( + const char *input, + size_t inputSize, + unsigned char *output, + size_t outputSize + ); + + namespace base64 { + String encode (const Vector&); + String encode (const String&); + String decode (const String&); + } + + class ArrayBuffer { + public: + using Pointer = SharedPointer; + mutable Mutex mutex; + Pointer bytes = nullptr; + Atomic byteOffset = 0; + Atomic byteLength = 0; + + ArrayBuffer () = default; + ArrayBuffer (size_t); + ArrayBuffer (size_t, const Pointer); + ArrayBuffer (size_t, size_t, const Pointer); + ArrayBuffer (size_t, size_t, const unsigned char*); + ArrayBuffer (const ArrayBuffer&); + ArrayBuffer (ArrayBuffer&&); + virtual ~ArrayBuffer (); + + ArrayBuffer& operator = (const ArrayBuffer&); + ArrayBuffer& operator = (ArrayBuffer&&); + ArrayBuffer& operator = (std::nullptr_t); + + size_t size () const; + const unsigned char* data () const; + unsigned char* data (); + const Pointer pointer () const; + Pointer pointer (); + ArrayBuffer slice (size_t, size_t = -1) const; + void resize (size_t); + }; + + class Buffer { + public: + static Buffer empty (); + + enum class Encoding { + UTF8, + HEX, + BASE64 + }; + + ArrayBuffer buffer; + Atomic byteOffset = 0; + Atomic byteLength = 0; + + Buffer () = default; + Buffer (const ArrayBuffer&); + Buffer (size_t, size_t, const ArrayBuffer&); + Buffer (const Buffer&); + Buffer (Buffer&&); + Buffer (const String&); + Buffer (size_t); + virtual ~Buffer (); + + Buffer& operator = (const Buffer&); + Buffer& operator = (Buffer&&); + + size_t size () const; + + template + bool set (const ByteArray&, size_t); + bool set (const Vector&, size_t); + bool set (const String&, size_t); + bool set (const Buffer&, size_t); + bool set (const ArrayBuffer&, size_t); + bool set (const unsigned char*, size_t, size_t); + bool set (const char*, size_t, size_t); + + unsigned char at (size_t) const; + const unsigned char* data () const; + unsigned char* data (); + Buffer slice (size_t, size_t = -1, bool = false) const; + String str (const Encoding = Encoding::UTF8) const; + }; + + class BufferQueue : public Buffer { + public: + template + bool push (const ByteArray&); + bool push (const Vector&); + bool push (const String&); + bool push (const Buffer&); + bool push (const ArrayBuffer&); + bool push (const unsigned char*, size_t); + bool push (const char*, size_t); + bool push (SharedPointer, size_t); + bool push (SharedPointer, size_t); + template + bool reset (const ByteArray&); + bool reset (const Vector&); + bool reset (const String&); + bool reset (const Buffer&); + bool reset (const ArrayBuffer&); + bool reset (const unsigned char*, size_t); + bool reset (const char*, size_t); + bool reset (SharedPointer, size_t); + bool reset (SharedPointer, size_t); + bool reset (); + }; +} +#endif diff --git a/src/runtime/bytes/base64.cc b/src/runtime/bytes/base64.cc new file mode 100644 index 0000000000..9c3a2d74f7 --- /dev/null +++ b/src/runtime/bytes/base64.cc @@ -0,0 +1,139 @@ +#include "../bytes.hh" + +namespace ssc::runtime::bytes { + static const char TABLE[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + static unsigned char base64_decode_char (char c) { + if (c >= 'A' && c <= 'Z') return c - 'A'; + if (c >= 'a' && c <= 'z') return c - 'a' + 26; + if (c >= '0' && c <= '9') return c - '0' + 52; + if (c == '+') return 62; + if (c == '/') return 63; + return 255; // invalid + } + + size_t base64_encode_length (size_t inputSize) { + return 4 * ((inputSize + 2) / 3); + } + + size_t base64_decode_length (size_t inputSize) { + return (inputSize / 4) * 3; + } + + size_t base64_encode ( + const unsigned char *input, + size_t inputSize, + char *output, + size_t outputSize + ) { + size_t encodedSize = 4 * ((inputSize + 2) / 3); + size_t i = 0; + size_t j = 0; + + if (outputSize < encodedSize + 1) { + return 0; + } + + while (i < inputSize) { + const uint32_t a = i < inputSize ? input[i++] : 0; + const uint32_t b = i < inputSize ? input[i++] : 0; + const uint32_t c = i < inputSize ? input[i++] : 0; + + const uint32_t triple = (a << 16) | (b << 8) | c; + + output[j++] = TABLE[(triple >> 18) & 0x3F]; + output[j++] = TABLE[(triple >> 12) & 0x3F]; + output[j++] = (i > inputSize + 1) ? '=' : TABLE[(triple >> 6) & 0x3F]; + output[j++] = (i > inputSize) ? '=' : TABLE[triple & 0x3F]; + } + + size_t paddingSize = (3 - (inputSize % 3)) % 3; + for (size_t p = 0; p < paddingSize; ++p) { + output[encodedSize - 1 - p] = '='; + } + + output[j] = 0; + return encodedSize; + } + + size_t base64_decode ( + const char *input, + size_t inputSize, + unsigned char *output, + size_t outputSize + ) { + if (inputSize % 4 != 0) { + return 0; + } + + size_t decodedSize = (inputSize / 4) * 3; + size_t i = 0; + size_t j = 0; + + if (input[inputSize - 1] == '=') decodedSize--; + if (input[inputSize - 2] == '=') decodedSize--; + + if (outputSize < decodedSize) { + return 0; + } + + while (i < inputSize) { + const auto a = base64_decode_char(input[i++]); + const auto b = base64_decode_char(input[i++]); + const auto c = base64_decode_char(input[i++]); + const auto d = base64_decode_char(input[i++]); + + const uint32_t triple = (a << 18) | (b << 12) | (c << 6) | d; + + if (j < decodedSize) output[j++] = (triple >> 16) & 0xFF; + if (j < decodedSize) output[j++] = (triple >> 8) & 0xFF; + if (j < decodedSize) output[j++] = triple & 0xFF; + } + + return decodedSize; + } + + namespace base64 { + String encode (const Vector& input) { + const size_t size = 4 * ((input.size() + 2) / 3) + 1; + String output(size, 0); + const auto encodedSize = base64_encode( + input.data(), + input.size(), + &output[0], + output.size() + ); + output.resize(encodedSize); + return output; + } + + String encode (const String& input) { + const size_t size = 4 * ((input.size() + 2) / 3) + 1; + String output(size, 0); + const auto encodedSize = base64_encode( + reinterpret_cast(input.data()), + input.size(), + &output[0], + output.size() + ); + output.resize(encodedSize); + return output; + } + + String decode (const String& input) { + const auto size = (input.size() / 4) * 3; + String output(size, 0); + const auto decodedSize = base64_decode( + input.data(), + input.size(), + reinterpret_cast(&output[0]), + output.size() + ); + output.resize(decodedSize); + return output; + } + } +} diff --git a/src/runtime/bytes/buffer.cc b/src/runtime/bytes/buffer.cc new file mode 100644 index 0000000000..552424514b --- /dev/null +++ b/src/runtime/bytes/buffer.cc @@ -0,0 +1,482 @@ +#include "../bytes.hh" + +namespace ssc::runtime::bytes { + ArrayBuffer::ArrayBuffer (size_t byteLength) + : byteLength(byteLength), + byteOffset(0), + bytes(new unsigned char[byteLength]{0}) + {} + + ArrayBuffer::ArrayBuffer ( + size_t byteLength, + const Pointer bytes + ) : byteLength(byteLength), + byteOffset(0), + bytes(bytes) + {} + + ArrayBuffer::ArrayBuffer ( + size_t byteLength, + size_t byteOffset, + const Pointer bytes + ) : byteLength(byteLength), + byteOffset(byteOffset), + bytes(bytes) + {} + + ArrayBuffer::ArrayBuffer (const ArrayBuffer& arrayBuffer) { + Lock lock(arrayBuffer.mutex); + this->byteLength = arrayBuffer.byteLength.load(); + this->byteOffset = arrayBuffer.byteOffset.load(); + this->bytes = arrayBuffer.bytes; + } + + ArrayBuffer::ArrayBuffer (ArrayBuffer&& arrayBuffer) { + Lock lock(arrayBuffer.mutex); + this->byteLength = arrayBuffer.byteLength.load(); + this->byteOffset = arrayBuffer.byteOffset.load(); + this->bytes = std::move(arrayBuffer.bytes); + arrayBuffer.byteLength = 0; + arrayBuffer.byteOffset = 0; + arrayBuffer.bytes = nullptr; + } + + ArrayBuffer::~ArrayBuffer () {} + + ArrayBuffer& ArrayBuffer::operator = (const ArrayBuffer& arrayBuffer) { + if (this == &arrayBuffer) { + return *this; + } + + ScopedLock lock(this->mutex, arrayBuffer.mutex); + this->byteLength = arrayBuffer.byteLength.load(); + this->byteOffset = arrayBuffer.byteOffset.load(); + this->bytes = arrayBuffer.bytes; + return *this; + } + + ArrayBuffer& ArrayBuffer::operator = (ArrayBuffer&& arrayBuffer) { + ScopedLock lock(this->mutex, arrayBuffer.mutex); + this->byteLength = arrayBuffer.byteLength.load(); + this->byteOffset = arrayBuffer.byteOffset.load(); + this->bytes = std::move(arrayBuffer.bytes); + arrayBuffer.byteLength = 0; + arrayBuffer.byteOffset = 0; + arrayBuffer.bytes = nullptr; + return *this; + } + + ArrayBuffer& ArrayBuffer::operator = (std::nullptr_t) { + this->resize(0); + return *this; + } + + size_t ArrayBuffer::size () const { + return this->byteLength; + } + + const unsigned char* ArrayBuffer::data () const { + return this->bytes.get() + this->byteOffset; + } + + unsigned char* ArrayBuffer::data () { + return this->bytes.get() + this->byteOffset; + } + + const ArrayBuffer::Pointer ArrayBuffer::pointer () const { + return this->bytes; + } + + ArrayBuffer::Pointer ArrayBuffer::pointer () { + return this->bytes; + } + + ArrayBuffer ArrayBuffer::slice (size_t begin, size_t end) const { + Lock lock(this->mutex); + if (begin < 0) { + begin = this->size() -1 + begin; + } + + if (end < 0) { + end = this->size() -1 + end; + } + + if (end < begin) { + end = begin; + } + + if (begin > end) { + begin = end; + } + + return ArrayBuffer( + end - begin, + this->byteOffset + begin, + this->bytes + ); + } + + void ArrayBuffer::resize (size_t size) { + if (size < 0) { + size = this->size() -1 + size; + } + + if (size == 0) { + this->byteOffset = 0; + this->byteLength = 0; + this->bytes = nullptr; + } else if (size > this->byteLength) { + if (this->bytes) { + const auto bytes = std::make_shared(size); + memset(bytes.get(), 0, size); + memcpy( + bytes.get(), + this->bytes.get() + this->byteOffset, + this->byteLength + ); + + this->byteLength = size; + this->byteOffset = 0; + this->bytes = bytes; + } else { + this->byteLength = size; + this->byteOffset = 0; + this->bytes = std::make_shared(size); + } + } else if (size < this->byteLength) { + this->byteLength = size; + } + } + + Buffer Buffer::empty () { + return Buffer(0); + } + + Buffer::Buffer (const ArrayBuffer& buffer) + : byteLength(buffer.byteLength.load(std::memory_order_relaxed)), + byteOffset(0), + buffer(buffer) + {} + + Buffer::Buffer ( + size_t byteLength, + size_t byteOffset, + const ArrayBuffer& buffer + ) : byteLength(byteLength), + byteOffset(byteOffset), + buffer(buffer) + {} + + Buffer::Buffer (const Buffer& buffer) + : byteLength(buffer.byteLength.load()), + byteOffset(buffer.byteOffset.load()), + buffer(buffer.buffer) + {} + + Buffer::Buffer (Buffer&& buffer) + : byteLength(buffer.byteLength.load()), + byteOffset(buffer.byteOffset.load()), + buffer(buffer.buffer) + { + buffer.byteOffset = 0; + buffer.byteLength = 0; + buffer.buffer = nullptr; + } + + Buffer::Buffer (const String& string) + : byteLength(string.size()), + byteOffset(0) + { + this->buffer.resize(string.size()); + memcpy(this->buffer.data(), string.c_str(), string.size()); + } + + Buffer::Buffer (size_t size) + : byteLength(size), + byteOffset(0), + buffer(size) + {} + + Buffer::~Buffer () { + } + + Buffer& Buffer::operator = (const Buffer&) { + return *this; + } + + Buffer& Buffer::operator = (Buffer&&) { + return *this; + } + + size_t Buffer::size () const { + return this->byteLength.load(std::memory_order_relaxed); + } + + template + bool Buffer::set (const ByteArray& input, size_t byteOffset) { + if (byteOffset + size > this->byteLength) { + throw new Error("Buffer::at: RangeError: 'size' and 'byteOffset' exceeds 'byteLength'"); + } + + memcpy(this->buffer.data() + byteOffset, input.data(), input.size()); + return true; + } + + bool Buffer::set (const Vector& input, size_t byteOffset) { + if (byteOffset + input.size() > this->byteLength) { + throw new Error("Buffer::at: RangeError: 'size' and 'byteOffset' exceeds 'byteLength'"); + } + memcpy(this->buffer.data() + byteOffset, input.data(), input.size()); + return true; + } + + bool Buffer::set (const String& input, size_t byteOffset) { + if (byteOffset + input.size() > this->byteLength) { + throw new Error("Buffer::at: RangeError: 'size' and 'byteOffset' exceeds 'byteLength'"); + } + memcpy(this->buffer.data() + byteOffset, input.data(), input.size()); + return true; + } + + bool Buffer::set (const Buffer& input, size_t byteOffset) { + if (byteOffset + input.size() > this->byteLength) { + throw new Error("Buffer::at: RangeError: 'size' and 'byteOffset' exceeds 'byteLength'"); + } + memcpy(this->buffer.data() + byteOffset, input.data(), input.size()); + return true; + } + + bool Buffer::set (const ArrayBuffer& input, size_t byteOffset) { + if (byteOffset + input.size() > this->byteLength) { + throw new Error("Buffer::at: RangeError: 'size' and 'byteOffset' exceeds 'byteLength'"); + } + memcpy(this->buffer.data() + byteOffset, input.data(), input.size()); + return true; + } + + bool Buffer::set (const unsigned char* input, size_t byteOffset, size_t byteLength) { + if (byteOffset + byteLength > this->byteLength) { + throw new Error("Buffer::at: RangeError: 'size' and 'byteOffset' exceeds 'byteLength'"); + } + memcpy(this->buffer.data() + byteOffset, input, byteLength); + return true; + } + + bool Buffer::set (const char* input, size_t byteOffset, size_t byteLength) { + if (byteOffset + byteLength > this->byteLength) { + throw new Error("Buffer::at: RangeError: 'size' and 'byteOffset' exceeds 'byteLength'"); + } + memcpy(this->buffer.data() + byteOffset, input, byteLength); + return true; + } + + unsigned char Buffer::at (size_t size) const { + if (size >= this->byteLength) { + throw new Error("Buffer::at: RangeError: 'size' exceeds 'byteLength'"); + } + + const auto data = this->buffer.data(); + if (data == nullptr) { + return 0; + } + + return data[size]; + } + + const unsigned char* Buffer::data () const { + return this->buffer.data() + this->byteOffset; + } + + unsigned char* Buffer::data () { + return this->buffer.data() + this->byteOffset; + } + + Buffer Buffer::slice (size_t begin, size_t end, bool copy) const { + if (begin < 0) { + begin = this->size() -1 + begin; + } + + if (end < 0) { + end = this->size() -1 + end; + } + + if (begin >= this->byteLength) { + throw new Error("Buffer::slice: RangeError: 'begin' exceeds 'byteLength'"); + } + + if (end >= this->byteLength) { + throw new Error("Buffer::slice: RangeError: 'end' exceeds 'byteLength'"); + } + + if (begin > end) { + throw new Error("Buffer::slice: RangeError: 'end' cannot be less than 'begin'"); + } + + if (!copy) { + return Buffer( + end - begin, + this->byteOffset + begin, + this->buffer + ); + } + + auto slice = this->buffer.slice( + this->byteOffset + begin, + this->byteOffset + begin + end + ); + + auto buffer = ArrayBuffer(slice.size()); + memcpy(buffer.data(), slice.data(), slice.size()); + return buffer; + } + + String Buffer::str (const Encoding encoding) const { + const auto string = reinterpret_cast(this->buffer.data()); + if (encoding == Encoding::HEX) { + return encodeHexString(string); + } else if (encoding == Encoding::BASE64) { + return base64::encode(string); + } + return string; + } + + template + bool BufferQueue::push (const ByteArray& input) { + this->buffer.resize(this->byteLength + size); + if (this->set(input, this->byteLength)) { + this->byteLength += size; + return true; + } + return false; + } + + bool BufferQueue::push (const Vector& input) { + this->buffer.resize(this->byteLength + input.size()); + if (this->set(input, this->byteLength)) { + this->byteLength += input.size(); + return true; + } + return false; + } + + bool BufferQueue::push (const String& input) { + this->buffer.resize(this->byteLength + input.size()); + return this->set(input, this->byteLength); + } + + bool BufferQueue::push (const Buffer& input) { + this->buffer.resize(this->byteLength + input.size()); + if (this->set(input, this->byteLength)) { + this->byteLength += input.size(); + return true; + } + return false; + } + + bool BufferQueue::push (const ArrayBuffer& input) { + this->buffer.resize(this->byteLength + input.size()); + if (this->set(input, this->byteLength)) { + this->byteLength += input.size(); + return true; + } + return false; + } + + bool BufferQueue::push (const unsigned char* input, size_t size) { + this->buffer.resize(this->byteLength + size); + if (this->set(input, this->byteLength, size)) { + this->byteLength += size; + return true; + } + return false; + } + + bool BufferQueue::push (SharedPointer input, size_t size) { + this->buffer.resize(this->byteLength + size); + if (this->set(input.get(), this->byteLength, size)) { + this->byteLength += size; + return true; + } + return false; + } + + bool BufferQueue::push (const char* input, size_t size) { + this->buffer.resize(this->byteLength + size); + if (this->set(input, this->byteLength, size)) { + this->byteLength += size; + return true; + } + return false; + } + + bool BufferQueue::push (SharedPointer input, size_t size) { + this->buffer.resize(this->byteLength + size); + if (this->set(input.get(), this->byteLength, size)) { + this->byteLength += size; + return true; + } + return false; + } + + template + bool BufferQueue::reset (const ByteArray& input) { + this->buffer.resize(size); + this->set(input.get(), 0, size); + return true; + } + + bool BufferQueue::reset (const Vector& input) { + this->buffer.resize(input.size()); + this->set(input, 0); + return true; + } + + bool BufferQueue::reset (const String& input) { + this->buffer.resize(input.size()); + this->set(input, 0); + return true; + } + + bool BufferQueue::reset (const Buffer& input) { + this->buffer.resize(input.size()); + this->set(input, 0); + return true; + } + + bool BufferQueue::reset (const ArrayBuffer& input) { + this->buffer.resize(input.size()); + this->set(input, 0); + return true; + } + + bool BufferQueue::reset (const unsigned char* input, size_t size) { + this->buffer.resize(size); + this->set(input, 0, size); + return true; + } + + bool BufferQueue::reset (SharedPointer input, size_t size) { + this->buffer.resize(size); + this->set(input.get(), 0, size); + return true; + } + + bool BufferQueue::reset (const char* input, size_t size) { + this->buffer.resize(size); + this->set(input, 0, size); + return true; + } + + bool BufferQueue::reset (SharedPointer input, size_t size) { + this->buffer.resize(size); + this->set(input.get(), 0, size); + return true; + } + + bool BufferQueue::reset () { + this->buffer.resize(0); + this->byteOffset = 0; + this->byteLength = 0; + return true; + } +} diff --git a/src/runtime/bytes/hex.cc b/src/runtime/bytes/hex.cc new file mode 100644 index 0000000000..9c31a88440 --- /dev/null +++ b/src/runtime/bytes/hex.cc @@ -0,0 +1,104 @@ +#include + +#include "../platform.hh" +#include "../bytes.hh" + +#define UNSIGNED_IN_RANGE(value, min, max) ( \ + (unsigned char) (value) >= (unsigned char) (min) && \ + (unsigned char) (value) <= (unsigned char) (max) \ +) + +static const char HEX_CHARS_TABLE[16 + 1] = "0123456789ABCDEF"; +static const signed char DEC_CHARS_INDEX[256] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ + /* 0 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* 1 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* 2 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* 3 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1, + + /* 4 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* 5 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* 6 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* 7 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + + /* 8 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* 9 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* A */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* B */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + + /* C */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* D */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* E */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* F */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1 +}; + +namespace ssc::runtime::bytes { + const Array toByteArray (const uint64_t input) { + Array bytes; + // big endian, network order + bytes[0] = input >> 8*7; + bytes[1] = input >> 8*6; + bytes[2] = input >> 8*5; + bytes[3] = input >> 8*4; + bytes[4] = input >> 8*3; + bytes[5] = input >> 8*2; + bytes[6] = input >> 8*1; + bytes[7] = input >> 8*0; + return bytes; + } + + const Array toByteArray (const uint32_t input) { + Array bytes; + // big endian, network order + bytes[0] = input >> 4*7; + bytes[1] = input >> 4*6; + bytes[2] = input >> 4*5; + bytes[3] = input >> 4*4; + return bytes; + } + + const Array toByteArray (const uint16_t input) { + Array bytes; + // big endian, network order + bytes[0] = input >> 2*7; + bytes[1] = input >> 2*6; + return bytes; + } + + String encodeHexString (const String& input) { + String output; + const auto length = 2 * input.size(); + + output.reserve(length); + + for (unsigned char character : input) { + output.push_back(HEX_CHARS_TABLE[character >> 4]); + output.push_back(HEX_CHARS_TABLE[character & 15]); + } + + return output; + } + + String encodeHexString (const Vector& input) { + std::ostringstream oss; + oss << std::hex << std::setfill('0'); + for (const auto c : input) { + oss << std::setw(2) << static_cast(c); + } + return oss.str(); + } + + String decodeHexString (const String& input) { + const auto length = input.length() / 2; + String output; + + output.reserve(length); + + for (auto character = input.begin(); character != input.end();) { + const int hi = DEC_CHARS_INDEX[(unsigned char) *character++]; + const int lo = DEC_CHARS_INDEX[(unsigned char) *character++]; + output.push_back(hi << 4 | lo); + } + + return output; + } +} diff --git a/src/core/color.hh b/src/runtime/color.hh similarity index 98% rename from src/core/color.hh rename to src/runtime/color.hh index e742dfb953..1ba4222630 100644 --- a/src/core/color.hh +++ b/src/runtime/color.hh @@ -1,9 +1,9 @@ -#ifndef SOCKET_RUNTIME_CORE_COLOR_H -#define SOCKET_RUNTIME_CORE_COLOR_H +#ifndef SOCKET_RUNTIME_RUNTIME_COLOR_H +#define SOCKET_RUNTIME_RUNTIME_COLOR_H #include "json.hh" -namespace SSC { +namespace ssc::runtime::color { /** * A container for RGB color components with an alpha channel */ diff --git a/src/runtime/color/color.cc b/src/runtime/color/color.cc new file mode 100644 index 0000000000..03d2472de8 --- /dev/null +++ b/src/runtime/color/color.cc @@ -0,0 +1,65 @@ +#include "../string.hh" +#include "../color.hh" + +using namespace ssc::runtime::string; + +namespace ssc::runtime::color { + Color::Color (const ColorComponents&) {} + + Color::Color ( + unsigned int red, + unsigned int green, + unsigned int blue, + float alpha + ) : ColorComponents(red, green, blue, alpha) + {} + + Color::Color (const Color& color) + : Color(color.red, color.green, color.blue, color.alpha) + {} + + Color::Color (Color&& color) + : Color(color.red, color.green, color.blue, color.alpha) + { + color.red = 0; + color.green = 0; + color.blue = 0; + color.alpha = 0.0f; + } + + Color& Color::operator = (const Color& color) { + this->red = color.red; + this->green = color.green; + this->blue = color.blue; + this->alpha = color.alpha; + return *this; + } + + Color& Color::operator = (Color&& color) { + this->red = color.red; + this->green = color.green; + this->blue = color.blue; + this->alpha = color.alpha; + color.red = 0; + color.green = 0; + color.blue = 0; + color.alpha = 0.0f; + return *this; + } + + bool Color::operator == (const Color& color) const { + return this->pack() == color.pack(); + } + + bool Color::operator != (const Color& color) const { + return this->pack() != color.pack(); + } + + bool Color::operator > (const Color& color) const { + return this->pack() > color.pack(); + } + + bool Color::operator < (const Color& color) const { + return this->pack() < color.pack(); + } +} diff --git a/src/core/color.cc b/src/runtime/color/components.cc similarity index 83% rename from src/core/color.cc rename to src/runtime/color/components.cc index 704510cf4d..4d27422df3 100644 --- a/src/core/color.cc +++ b/src/runtime/color/components.cc @@ -1,7 +1,9 @@ -#include "../platform/string.hh" -#include "color.hh" +#include "../string.hh" +#include "../color.hh" -namespace SSC { +using namespace ssc::runtime::string; + +namespace ssc::runtime::color { template static inline T clamp (T v, T x, T y) { if (v < x) { @@ -227,7 +229,7 @@ namespace SSC { break; } - return tmpl(output, Map { + return tmpl(output, Map { {"red", std::to_string(this->red)}, {"green", std::to_string(this->green)}, {"blue", std::to_string(this->blue)}, @@ -273,64 +275,4 @@ namespace SSC { return 0; } - - Color::Color (const ColorComponents&) { - } - - Color::Color ( - unsigned int red, - unsigned int green, - unsigned int blue, - float alpha - ) : ColorComponents(red, green, blue, alpha) - {} - - Color::Color (const Color& color) - : Color(color.red, color.green, color.blue, color.alpha) - {} - - Color::Color (Color&& color) - : Color(color.red, color.green, color.blue, color.alpha) - { - color.red = 0; - color.green = 0; - color.blue = 0; - color.alpha = 0.0f; - } - - Color& Color::operator = (const Color& color) { - this->red = color.red; - this->green = color.green; - this->blue = color.blue; - this->alpha = color.alpha; - return *this; - } - - Color& Color::operator = (Color&& color) { - this->red = color.red; - this->green = color.green; - this->blue = color.blue; - this->alpha = color.alpha; - color.red = 0; - color.green = 0; - color.blue = 0; - color.alpha = 0.0f; - return *this; - } - - bool Color::operator == (const Color& color) const { - return this->pack() == color.pack(); - } - - bool Color::operator != (const Color& color) const { - return this->pack() != color.pack(); - } - - bool Color::operator > (const Color& color) const { - return this->pack() > color.pack(); - } - - bool Color::operator < (const Color& color) const { - return this->pack() < color.pack(); - } } diff --git a/src/core/config.hh b/src/runtime/config.hh similarity index 93% rename from src/core/config.hh rename to src/runtime/config.hh index bce1a96a8d..c6c3fb596c 100644 --- a/src/core/config.hh +++ b/src/runtime/config.hh @@ -1,5 +1,5 @@ -#ifndef SOCKET_RUNTIME_CORE_CONFIG_H -#define SOCKET_RUNTIME_CORE_CONFIG_H +#ifndef SOCKET_RUNTIME_RUNTIME_CONFIG_H +#define SOCKET_RUNTIME_RUNTIME_CONFIG_H #include @@ -32,7 +32,7 @@ #endif #if defined(__cplusplus) -#include "../platform/platform.hh" +#include "platform.hh" extern "C" { // implemented in `init.cc` @@ -43,8 +43,8 @@ extern "C" { extern int socket_runtime_init_get_dev_port (); } -namespace SSC { - const Map getUserConfig (); +namespace ssc::runtime::config { + const Map getUserConfig (); bool isDebugEnabled (); const String getDevHost (); int getDevPort (); @@ -58,9 +58,9 @@ namespace SSC { * Internal configuration mapping, exposed as a const reference to the * caller in `Config::data()` */ - Map map; + Map map; public: - using Iterator = Map::const_iterator; + using Iterator = Map::const_iterator; using Path = Vector; /** @@ -73,9 +73,9 @@ namespace SSC { */ Config () = default; Config (const String& source); - Config (const Map& source); + Config (const Map& source); Config (const Config& source); - Config (const String& prefix, const Map& source); + Config (const String& prefix, const Map& source); Config (const String& prefix, const Config& source); /** @@ -124,7 +124,7 @@ namespace SSC { * * @return `Map&` A reference to the internal data map */ - const Map& data () const noexcept; + const Map& data () const noexcept; /** * Get a `Config` instance as a "slice" of this configuration, such as diff --git a/src/core/config.cc b/src/runtime/config/config.cc similarity index 90% rename from src/core/config.cc rename to src/runtime/config/config.cc index b0598c45a8..4854a65c16 100644 --- a/src/core/config.cc +++ b/src/runtime/config/config.cc @@ -1,31 +1,14 @@ -#include "config.hh" -#include "debug.hh" -#include "ini.hh" +#include "../config.hh" +#include "../string.hh" +#include "../debug.hh" +#include "../ini.hh" -namespace SSC { +using namespace ssc::runtime::string; + +namespace ssc::runtime::config { static constexpr char NAMESPACE_SEPARATOR = '.'; static const String NAMESPACE_SEPARATOR_STRING = String(1, NAMESPACE_SEPARATOR); - bool isDebugEnabled () { - return socket_runtime_init_is_debug_enabled(); - } - - const Map getUserConfig () { - const auto bytes = socket_runtime_init_get_user_config_bytes(); - const auto size = socket_runtime_init_get_user_config_bytes_size(); - const auto string = String(reinterpret_cast(bytes), size); - const auto userConfig = INI::parse(string); - return userConfig; - } - - const String getDevHost () { - return socket_runtime_init_get_dev_host(); - } - - int getDevPort () { - return socket_runtime_init_get_dev_port(); - } - Config::Config (const String& source) { this->map = INI::parse(source, NAMESPACE_SEPARATOR_STRING); } @@ -34,11 +17,13 @@ namespace SSC { this->map = source.data(); } - Config::Config (const Map& source) { + Config::Config (const Map& source) { this->map = source; } - Config::Config (const String& prefix, const Map& source) : prefix(prefix) { + Config::Config (const String& prefix, const Map& source) + : prefix(prefix) + { this->map = source; } @@ -93,13 +78,13 @@ namespace SSC { return erased; } - const Map& Config::data () const noexcept { + const Map& Config::data () const noexcept { return this->map; } const Config Config::slice (const String& key) const noexcept { const auto view = this->query("[" + key + "]"); - Map slice; + Map slice; for (const auto& tuple : view) { if ( @@ -132,7 +117,7 @@ namespace SSC { String query = trim(input); State state; - Map results; + Map results; if (!query.starts_with("[") && !query.starts_with(NAMESPACE_SEPARATOR_STRING)) { query = "[" + query + "]"; diff --git a/src/runtime/config/global.cc b/src/runtime/config/global.cc new file mode 100644 index 0000000000..f75943de1f --- /dev/null +++ b/src/runtime/config/global.cc @@ -0,0 +1,24 @@ +#include "../config.hh" +#include "../ini.hh" + +namespace ssc::runtime::config { + bool isDebugEnabled () { + return socket_runtime_init_is_debug_enabled(); + } + + const Map getUserConfig () { + const auto bytes = socket_runtime_init_get_user_config_bytes(); + const auto size = socket_runtime_init_get_user_config_bytes_size(); + const auto string = String(reinterpret_cast(bytes), size); + const auto userConfig = INI::parse(string); + return userConfig; + } + + const String getDevHost () { + return socket_runtime_init_get_dev_host(); + } + + int getDevPort () { + return socket_runtime_init_get_dev_port(); + } +} diff --git a/src/runtime/context.hh b/src/runtime/context.hh new file mode 100644 index 0000000000..e32a63c914 --- /dev/null +++ b/src/runtime/context.hh @@ -0,0 +1,97 @@ +#ifndef SOCKET_RUNTIME_CONTEXT_H +#define SOCKET_RUNTIME_CONTEXT_H + +#include "webview/webview.hh" +#include "queued_response.hh" +#include "platform.hh" +#include "loop.hh" + +namespace ssc::runtime { + class Runtime; +} + +namespace ssc::runtime::context { + struct Context { + Mutex mutex; + + #if SOCKET_RUNTIME_PLATFORM_ANDROID + android::ContentResolver contentResolver; + android::JVMEnvironment jvm; + android::Activity activity = nullptr; + android::Looper looper; + bool isEmulator = false; + #endif + + Context () = default; + Context (const Context&) = delete; + Context (Context&&) = delete; + + Context& operator = (const Context&) = delete; + Context& operator = (Context&&) = delete; + + #if SOCKET_RUNTIME_PLATFORM_ANDROID + void Platform::configureAndroid ( + android::JVMEnvironment jvm, + android::Activity activity + ); + #endif + }; + + /** + * The `RuntimeContext` struct represents a base class for `Runtime` or + * other suitable candidates can extend from. This base class will contain + * public state for the execution context in which it is active in. + * This includes platform dependent information, APIs, and references. + */ + struct RuntimeContext : public Context { + // fka 'Posts' - this is a container for "queued" responses, most likely + // sent to a webview through a javascript evaluation and a + // `ipc://queuedResponse` XHR request + QueuedResponses queuedResponses; + loop::Loop loop; + Mutex mutex; + + RuntimeContext () = default; + RuntimeContext (const RuntimeContext&) = delete; + RuntimeContext (RuntimeContext&&) = delete; + + RuntimeContext& operator = (const RuntimeContext&) = delete; + RuntimeContext& operator = (RuntimeContext&&) = delete; + + RuntimeContext* getRuntimeContext (); + Runtime* getRuntime (); + String createQueuedResponse ( + const String& seq, + const String& params, + QueuedResponse queuedResponse + ); + + #if SOCKET_RUNTIME_PLATFORM_ANDROID + void Platform::configureAndroid ( + android::JVMEnvironment jvm, + android::Activity activity + ); + #endif + }; + + struct DispatchContext : public Context { + RuntimeContext& context; + DispatchContext () = delete; + DispatchContext (RuntimeContext&); + DispatchContext (const DispatchContext&) = delete; + DispatchContext (DispatchContext&&) = delete; + virtual ~DispatchContext (); + DispatchContext& operator = (const DispatchContext&) = delete; + DispatchContext& operator = (DispatchContext&&) = delete; + }; + + class Dispatcher : public DispatchContext { + public: + using Callback = Function; + Dispatcher (RuntimeContext&); + bool dispatch (const Callback); + }; + + using DispatchCallback = Dispatcher::Callback; +} +#endif diff --git a/src/runtime/context/context.cc b/src/runtime/context/context.cc new file mode 100644 index 0000000000..edecd05b6a --- /dev/null +++ b/src/runtime/context/context.cc @@ -0,0 +1,74 @@ +#include "../javascript.hh" +#include "../runtime.hh" +#include "../string.hh" + +#include "../context.hh" + +using ssc::runtime::javascript::createJavaScript; +using ssc::runtime::string::trim; + +namespace ssc::runtime::context { +#if SOCKET_RUNTIME_PLATFORM_ANDROID + void Context::configureAndroid ( + Android::JVMEnvironment jvm, + Android::Activity activity + ) { + this->jvm = jvm; + this->activity = activity; + this->contentResolver.activity = activity; + this->contentResolver.jvm = jvm; + } +#endif + + RuntimeContext* RuntimeContext::getRuntimeContext () { + return this; + } + + Runtime* RuntimeContext::getRuntime () { + return static_cast(this); + } + + String RuntimeContext::createQueuedResponse ( + const String& seq, + const String& params, + QueuedResponse queuedResponse + ) { + if (queuedResponse.id == 0) { + queuedResponse.id = rand64(); + } + + const auto script = createJavaScript("queued-response.js", + "const globals = await import('socket:internal/globals'); \n" + "const id = `" + std::to_string(queuedResponse.id) + "`; \n" + "const seq = `" + seq + "`; \n" + "const workerId = `" + queuedResponse.workerId + "`.trim() || null; \n" + "const headers = `" + trim(queuedResponse.headers.str()) + "` \n" + " .trim() \n" + " .split(/[\\r\\n]+/) \n" + " .filter(Boolean) \n" + " .map((header) => header.trim()); \n" + " \n" + "let params = `" + params + "`; \n" + " \n" + "try { \n" + " params = JSON.parse(params); \n" + "} catch (err) { \n" + " console.error(err.stack || err, params); \n" + "} \n" + " \n" + "globals.get('RuntimeQueuedResponses').dispatch( \n" + " id, \n" + " seq, \n" + " params, \n" + " headers, \n" + " { workerId } \n" + "); \n" + ); + + Lock lock(this->mutex); + this->queuedResponses[queuedResponse.id] = std::move(queuedResponse); + return script; + } + + DispatchContext::~DispatchContext () {} +} diff --git a/src/runtime/context/dispatch.cc b/src/runtime/context/dispatch.cc new file mode 100644 index 0000000000..22cf445607 --- /dev/null +++ b/src/runtime/context/dispatch.cc @@ -0,0 +1,77 @@ +#include "../core.hh" + +namespace ssc::runtime::context { + DispatchContext::DispatchContext (RuntimeContext& context) + : context(context) + {} + + DispatchContext::DispatchContext (const DispatchContext& context) + : context(context.context) + {} + + Dispatcher::Dispatcher (RuntimeContext& context) + : DispatchContext(context) + {} + + bool Dispatcher::dispatch (const Callback callback) { + if (callback == nullptr) { + return false; + } + + #if SOCKET_RUNTIME_PLATFORM_LINUX + g_main_context_invoke( + nullptr, + +[](gpointer userData) -> gboolean { + const auto callback = reinterpret_cast(userData); + if (*callback != nullptr) { + (*callback)(); + delete callback; + } + return G_SOURCE_REMOVE; + }, + new DispatchCallback(std::move(callback)) + ); + + return true; + #elif SOCKET_RUNTIME_PLATFORM_APPLE + auto priority = DISPATCH_QUEUE_PRIORITY_DEFAULT; + auto queue = dispatch_get_global_queue(priority, 0); + + dispatch_async(queue, ^{ + dispatch_sync(dispatch_get_main_queue(), ^{ + callback(); + }); + }); + + return true; + #elif SOCKET_RUNTIME_PLATFORM_WINDOWS + static auto mainThread = GetCurrentThreadId(); + auto threadCallback = (LPARAM) new DispatchCallback(std::move(callback)); + + if (this->flags.ready.load(std::memory_order_relaxed)) { + PostThreadMessage(mainThread, WM_APP, 0, threadCallback); + return; + } + + Thread t([&, threadCallback] { + // TODO(trevnorris,jwerle): Need to also check a shouldExit so this doesn't run forever in case + // the rest of the application needs to exit before isReady is set. + while (!this->flags.ready.load(std::memory_order_relaxed) { + msleep(16); + } + + PostThreadMessage(mainThread, WM_APP, 0, threadCallback); + }); + + t.detach(); + return true; + #elif SOCKET_RUNTIME_PLATFORM_ANDROID + this->looper.dispatch([this, callback = std::move(callback)] () { + const auto attachment = Android::JNIEnvironmentAttachment(this->jvm); + callback(); + }); + return true; + #endif + return false; + } +} diff --git a/src/core/module.hh b/src/runtime/core.hh similarity index 68% rename from src/core/module.hh rename to src/runtime/core.hh index 2fb95c4b9f..544813a161 100644 --- a/src/core/module.hh +++ b/src/runtime/core.hh @@ -1,25 +1,31 @@ -#ifndef SOCKET_RUNTIME_CORE_MODULE_H -#define SOCKET_RUNTIME_CORE_MODULE_H +#ifndef SOCKET_RUNTIME_CORE_H +#define SOCKET_RUNTIME_CORE_H -#include "../platform/platform.hh" +#include "queued_response.hh" +#include "platform.hh" +#include "context.hh" +#include "options.hh" #include "json.hh" -#include "post.hh" +#include "loop.hh" -namespace SSC { - // forward - class Core; - class CoreModule { +namespace ssc::runtime::core { + class Services; + + struct DispatchContext : public context::DispatchContext { + struct StateFlags { + Atomic ready = false; + }; + + StateFlags flags; + }; + + class Service { public: - using Callback = Function; + using Callback = Function; struct RequestContext { String seq; - CoreModule::Callback callback; - RequestContext () = default; - RequestContext (String seq, const CoreModule::Callback& callback) { - this->seq = seq; - this->callback = callback; - } + Callback callback; }; template @@ -45,13 +51,13 @@ namespace SSC { observer.callback = nullptr; } - Observer (const Callback& callback) + Observer (const Callback callback) : callback(callback) { this->id = rand64(); } - Observer (uint64_t id, const Callback& callback) + Observer (uint64_t id, const Callback callback) : id(id), callback(callback) {} @@ -68,6 +74,12 @@ namespace SSC { observer.callback = nullptr; return *this; } + + JSON::Object json () const { + return JSON::Object::Entries { + {"id", this->id} + }; + } }; template @@ -105,17 +117,15 @@ namespace SSC { do { if (iterator->id == 0) { iterator = this->observers.erase(iterator); + } else { + iterator++; } if (iterator->id == observer.id) { iterator = this->observers.erase(iterator); return true; } - - } while ( - iterator != this->observers.end() && - ++iterator != this->observers.end() - ); + } while (iterator != this->observers.end()); return false; } @@ -129,7 +139,6 @@ namespace SSC { } return false; - } Observer& get (const uint64_t id) { @@ -160,19 +169,47 @@ namespace SSC { } else if (iterator->callback != nullptr) { iterator->callback(arguments...); dispatched = true; + iterator++; } - - iterator++; } return dispatched; } + + JSON::Array json () const { + return this->observers; + } + }; + + struct Options { + context::RuntimeContext& context; + bool enabled = false; + context::Dispatcher& dispatcher; + loop::Loop& loop; + Services& services; }; - Core *core = nullptr; - CoreModule (Core* core) - : core(core) - {} + context::RuntimeContext& context; + context::Dispatcher& dispatcher; + loop::Loop& loop; + + // ref back to `Services` container + Services& services; + + // state + Atomic enabled = false; + + Service (const Options&); + Service () = delete; + Service (const Service&) = delete; + Service (Service&&) = delete; + virtual ~Service () noexcept; + Service& operator = (const Service&) = delete; + Service& operator = (Service&&) = delete; + virtual bool start (); + virtual bool stop (); + bool dispatch (const context::Dispatcher::Callback); + context::RuntimeContext* getRuntimeContext (); }; } #endif diff --git a/src/runtime/core/service.cc b/src/runtime/core/service.cc new file mode 100644 index 0000000000..f737af3a6a --- /dev/null +++ b/src/runtime/core/service.cc @@ -0,0 +1,29 @@ +#include "../core.hh" + +namespace ssc::runtime::core { + Service::~Service () noexcept {} + + Service::Service (const Options& options) + : dispatcher(options.dispatcher), + services(options.services), + context(options.context), + enabled(options.enabled), + loop(options.loop) + {} + + bool Service::dispatch (const context::Dispatcher::Callback callback) { + return this->dispatcher.dispatch(callback); + } + + bool Service::start () { + return this->enabled.load(); + } + + bool Service::stop () { + return this->enabled.load(); + } + + context::RuntimeContext* Service::getRuntimeContext () { + return &this->context; + } +} diff --git a/src/runtime/core/services.cc b/src/runtime/core/services.cc new file mode 100644 index 0000000000..bbecd4cf4f --- /dev/null +++ b/src/runtime/core/services.cc @@ -0,0 +1,83 @@ +#include "services.hh" + +namespace ssc::runtime::core { + Services::Services ( + context::RuntimeContext& context, + const Options& options + ) + : ai({ context, options.features.useAI, options.dispatcher, context.loop, *this }), + conduit({ context, options.features.useConduit, options.dispatcher, context.loop, *this }), + broadcastChannel({ context, options.features.useBroadcashChannel, options.dispatcher, context.loop, *this }), + dns({ context, options.features.useDNS, options.dispatcher, context.loop, *this }), + diagnostics({ context, options.features.useDiagnostics, options.dispatcher, context.loop, *this }), + fs({ context, options.features.useFS, options.dispatcher, context.loop, *this }), + geolocation({ context, options.features.useGeolocation, options.dispatcher, context.loop, *this }), + mediaDevices({ context, options.features.useMediaDevices, options.dispatcher, context.loop, *this }), + networkStatus({ context, options.features.useNetworkStatus, options.dispatcher, context.loop, *this }), + notifications({ context, options.features.useNotifications, options.dispatcher, context.loop, *this }), + os({ context, options.features.useOS, options.dispatcher, context.loop, *this }), + permissions({ context, options.features.usePermissions, options.dispatcher, context.loop, *this }), + process({ context, options.features.useProcess, options.dispatcher, context.loop, *this }), + platform({ context, options.features.usePlatform, options.dispatcher, context.loop, *this }), + timers({ context, options.features.useTimers, options.dispatcher, context.loop, *this }), + udp({ context, options.features.useUDP, options.dispatcher, context.loop, *this }) + {} + + bool Services::start () { + Lock lock(this->mutex); + const auto services = Vector { + &this->ai, + &this->conduit, + &this->broadcastChannel, + &this->dns, + &this->diagnostics, + &this->fs, + &this->geolocation, + &this->mediaDevices, + &this->networkStatus, + &this->notifications, + &this->os, + &this->permissions, + &this->platform, + &this->process, + &this->timers, + &this->udp + }; + + for (const auto& service : services) { + if (service->enabled && !service->start()) { + return false; + } + } + return true; + } + + bool Services::stop () { + Lock lock(this->mutex); + const auto services = Vector { + &this->ai, + &this->conduit, + &this->broadcastChannel, + &this->dns, + &this->diagnostics, + &this->fs, + &this->geolocation, + &this->mediaDevices, + &this->networkStatus, + &this->notifications, + &this->os, + &this->permissions, + &this->platform, + &this->process, + &this->timers, + &this->udp + }; + + for (const auto& service : services) { + if (service->enabled && !service->stop()) { + return false; + } + } + return true; + } +} diff --git a/src/runtime/core/services.hh b/src/runtime/core/services.hh new file mode 100644 index 0000000000..64b864ff7a --- /dev/null +++ b/src/runtime/core/services.hh @@ -0,0 +1,82 @@ +#ifndef SOCKET_RUNTIME_CORE_SERVICES_H +#define SOCKET_RUNTIME_CORE_SERVICES_H + +#include "../context.hh" +#include "../options.hh" + +#include "services/ai.hh" +#include "services/broadcast_channel.hh" +#include "services/conduit.hh" +#include "services/diagnostics.hh" +#include "services/dns.hh" +#include "services/fs.hh" +#include "services/geolocation.hh" +#include "services/media_devices.hh" +#include "services/network_status.hh" +#include "services/notifications.hh" +#include "services/os.hh" +#include "services/permissions.hh" +#include "services/platform.hh" +#include "services/process.hh" +#include "services/timers.hh" +#include "services/udp.hh" + +namespace ssc::runtime::core { + struct Services { + struct Features { + bool useAI = true; + bool useBroadcashChannel = true; + #if !SOCKET_RUNTIME_PLATFORM_IOS + bool useChildProcess = true; + #endif + bool useConduit = true; + bool useDiagnostics = true; + bool useDNS = true; + bool useFS = true; + bool useGeolocation = true; + bool useMediaDevices = true; + bool useNetworkStatus = true; + bool useNotifications = true; + bool useOS = true; + bool usePermissions = true; + bool usePlatform = true; + bool useProcess = true; + bool useTimers = true; + bool useUDP = true; + }; + + struct Options { + context::Dispatcher& dispatcher; + Features features; + }; + + Mutex mutex; + + core::services::BroadcastChannel broadcastChannel; + core::services::AI ai; + core::services::Conduit conduit; + core::services::Diagnostics diagnostics; + core::services::DNS dns; + core::services::FS fs; + core::services::Geolocation geolocation; + core::services::MediaDevices mediaDevices; + core::services::NetworkStatus networkStatus; + core::services::Notifications notifications; + core::services::OS os; + core::services::Permissions permissions; + core::services::Platform platform; + core::services::Process process; + core::services::Timers timers; + core::services::UDP udp; + + Services (context::RuntimeContext&, const Options&); + Services () = delete; + Services (const Services&) = delete; + Services (Services&&) = delete; + Services& operator = (const Services&) = delete; + Services& operator = (Services&&) = delete; + bool start (); + bool stop (); + }; +} +#endif diff --git a/src/core/modules/ai.cc b/src/runtime/core/services/ai.cc similarity index 95% rename from src/core/modules/ai.cc rename to src/runtime/core/services/ai.cc index c6f121211a..81fddf7f18 100644 --- a/src/core/modules/ai.cc +++ b/src/runtime/core/services/ai.cc @@ -1,5 +1,6 @@ -#include "../core.hh" -#include "../resource.hh" +#include "../../filesystem.hh" +#include "../../url.hh" + #include "ai.hh" #if defined (_WIN32) @@ -8,11 +9,12 @@ #endif #endif +using ssc::runtime::url::encodeURIComponent; -namespace SSC { +namespace ssc::runtime::core::services { static JSON::Object::Entries ERR_AI_LLM_NOEXISTS ( const String& source, - CoreAI::ID id + AI::ID id ) { return JSON::Object::Entries { {"source", source}, @@ -27,7 +29,7 @@ namespace SSC { static JSON::Object::Entries ERR_AI_LLM_MESSAGE ( const String& source, - CoreAI::ID id, + AI::ID id, const String& message ) { return JSON::Object::Entries { @@ -43,7 +45,7 @@ namespace SSC { static JSON::Object::Entries ERR_AI_LLM_EXISTS ( const String& source, - CoreAI::ID id + AI::ID id ) { return JSON::Object::Entries { {"source", source}, @@ -56,7 +58,7 @@ namespace SSC { }; } - SharedPointer CoreAI::getLLM (ID id) { + SharedPointer AI::getLLM (ID id) { Lock lock(this->mutex); if (this->llms.contains(id)) { @@ -66,21 +68,21 @@ namespace SSC { return nullptr; } - bool CoreAI::hasLLM (ID id) { + bool AI::hasLLM (ID id) { Lock lock(this->mutex); return this->llms.find(id) != this->llms.end(); } - void CoreAI::createLLM( + void AI::createLLM ( const String& seq, ID id, LLMOptions options, - const CoreModule::Callback& callback + const Callback callback ) { - this->core->dispatchEventLoop([=, this] { + this->loop.dispatch([=, this] { if (this->hasLLM(id)) { auto json = ERR_AI_LLM_EXISTS("ai.llm.create", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } /* auto log = [&](String message) { @@ -92,13 +94,13 @@ namespace SSC { }} }; - callback("-1", json, Post{}); + callback("-1", json, QueuedResponse{}); }; */ auto llm = std::make_shared(options); if (llm->err.size()) { auto json = ERR_AI_LLM_MESSAGE("ai.llm.create", id, llm->err); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); return; } @@ -109,22 +111,22 @@ namespace SSC { }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); Lock lock(this->mutex); this->llms.emplace(id, llm); }); }; - void CoreAI::chatLLM( + void AI::chatLLM ( const String& seq, ID id, String message, - const CoreModule::Callback& callback + const Callback callback ) { - this->core->dispatchEventLoop([=, this] { + this->loop.dispatch([=, this] { if (!this->hasLLM(id)) { auto json = ERR_AI_LLM_NOEXISTS("ai.llm.chat", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto llm = this->getLLM(id); @@ -139,22 +141,22 @@ namespace SSC { }} }; - callback("-1", json, Post{}); + callback("-1", json, QueuedResponse{}); return isComplete; }); }); }; - void CoreAI::destroyLLM( + void AI::destroyLLM ( const String& seq, ID id, - const CoreModule::Callback& callback + const Callback callback ) { - this->core->dispatchEventLoop([=, this] { + this->loop.dispatch([=, this] { if (!this->hasLLM(id)) { auto json = ERR_AI_LLM_NOEXISTS("ai.llm.destroy", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } Lock lock(this->mutex); @@ -169,15 +171,15 @@ namespace SSC { }); }; - void CoreAI::stopLLM( + void AI::stopLLM ( const String& seq, ID id, - const CoreModule::Callback& callback + const Callback callback ) { - this->core->dispatchEventLoop([=, this] { + this->loop.dispatch([=, this] { if (!this->hasLLM(id)) { auto json = ERR_AI_LLM_NOEXISTS("ai.llm.stop", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto llm = this->getLLM(id); @@ -251,7 +253,7 @@ namespace SSC { this->params.n_ctx = 2048; - FileResource modelResource(options.path); + filesystem::Resource modelResource(options.path); if (!modelResource.exists()) { this->err = "Unable to access the model file due to permissions"; @@ -396,7 +398,7 @@ namespace SSC { llama_backend_free(); }; - void LLM::chat (String buffer, const Cb cb) { + void LLM::chat (String buffer, const ChatCallback cb) { this->stopped = false; int ga_i = 0; diff --git a/src/runtime/core/services/ai.hh b/src/runtime/core/services/ai.hh new file mode 100644 index 0000000000..510ec31708 --- /dev/null +++ b/src/runtime/core/services/ai.hh @@ -0,0 +1,104 @@ +#ifndef SOCKET_RUNTIME_CORE_SERVICES_AI_H +#define SOCKET_RUNTIME_CORE_SERVICES_AI_H + +#include +#include + +#include "../../core.hh" + +namespace ssc::runtime::core::services { + class LLM; + + struct LLMOptions { + bool conversation = false; + bool chatml = false; + int n_ctx = 0; + bool instruct = false; + int n_keep = 0; + int n_batch = 0; + int n_threads = 0; + int n_gpu_layers = 0; + int n_predict = 0; + int grp_attn_n = 0; + int grp_attn_w = 0; + int seed = 0; + int max_tokens = 0; + int top_k = 0; + float top_p = 0.0; + float min_p = 0.0; + float tfs_z = 0.0; + float typical_p = 0.0; + float temp; + + String path; + String prompt; + String antiprompt; + }; + + class AI : public core::Service { + public: + using ID = uint64_t; + using LLMs = Map>; + + Mutex mutex; + LLMs llms; + + AI (const Options& options) + : core::Service(options) + {} + + void chatLLM (const String&, ID, String, const Callback); + void createLLM (const String&, ID, LLMOptions, const Callback); + void destroyLLM (const String&, ID, const Callback); + void stopLLM (const String&, ID , const Callback); + bool hasLLM (ID id); + SharedPointer getLLM (ID id); + }; + + class LLM { + using ChatCallback = Function; + using Logger = Function; + + gpt_params params; + llama_model* model = nullptr; + llama_context* ctx = nullptr; + llama_context* guidance = nullptr; + struct llama_sampling_context* sampling; + + Vector* input_tokens = nullptr; + std::ostringstream* output_ss = nullptr; + Vector* output_tokens = nullptr; + Vector session_tokens; + Vector embd_inp; + Vector guidance_inp; + Vector> antiprompt_ids; + + String path_session = ""; + int guidance_offset = 0; + int original_prompt_len = 0; + int n_ctx = 0; + int n_past = 0; + int n_consumed = 0; + int n_session_consumed = 0; + int n_past_guidance = 0; + + public: + static Logger log; + static void tramp ( + ggml_log_level level, + const char* message, + void* user_data + ); + + String err = ""; + bool stopped = false; + bool interactive = false; + + LLM (LLMOptions options); + ~LLM(); + + void chat (String input, const ChatCallback cb); + void escape (String& input); + }; +} +#endif diff --git a/src/runtime/core/services/broadcast_channel.cc b/src/runtime/core/services/broadcast_channel.cc new file mode 100644 index 0000000000..1ff9b17e2a --- /dev/null +++ b/src/runtime/core/services/broadcast_channel.cc @@ -0,0 +1,100 @@ +#include "../../debug.hh" + +#include "broadcast_channel.hh" + +namespace ssc::runtime::core::services { + const JSON::Object BroadcastChannel::Message::json () const { + return JSON::Object::Entries { + {"origin", this->origin}, + {"token", this->token}, + {"name", this->name}, + {"data", this->data}, + {"id", this->id} + }; + } + + const JSON::Object BroadcastChannel::MessageEvent::json () const { + return JSON::Object::Entries { + {"subscription", this->subscription.json()}, + {"message", Message::json()}, + {"origin", this->origin}, + {"name", this->name}, + {"id", this->id} + }; + } + + const JSON::Object BroadcastChannel::Subscription::json () const { + return JSON::Object::Entries { + {"origin", this->origin}, + {"name", this->name}, + {"id", std::to_string(this->id)} + }; + } + + const String BroadcastChannel::Subscription::key () const { + if (this->origin.ends_with("/")) { + return this->origin + this->name; + } else { + return this->origin + "/" + this->name; + } + } + + const BroadcastChannel::Subscription& BroadcastChannel::subscribe ( + const Subscription& subscription + ) { + Lock lock(this->mutex); + this->subscriptions.emplace(subscription.id, subscription); + const auto& result = this->subscriptions.at(subscription.id); + const auto& callback = result.callback; + const auto& observer = this->subscriptionObservers[subscription.key()]; + auto& observers = this->observers[subscription.key()]; + + observers.add(observer, [this](const auto event) { + for (const auto& entry : this->subscriptions) { + if (entry.first == event.subscription.id) { + entry.second.callback(event); + } + } + }); + + return result; + } + + bool BroadcastChannel::unsubscribe (const SubscriptionID id) { + Lock lock(this->mutex); + if (this->subscriptions.contains(id)) { + this->subscriptions.erase(id); + } + return false; + } + + bool BroadcastChannel::postMessage (const Message& message) { + Lock lock(this->mutex); + bool dispatched = false; + int count = 0; + + for (const auto& entry : this->subscriptions) { + const auto& subscription = entry.second; + if (message.origin == "*") { + if (subscription.name == message.name) { + const auto event = MessageEvent {message, subscription}; + if (this->observers.at(subscription.key()).dispatch(event)) { + dispatched = true; + count++; + } + } + } else if ( + subscription.origin == message.origin && + subscription.name == message.name + ) { + const auto event = MessageEvent {message, subscription}; + if (this->observers[subscription.key()].dispatch(event)) { + dispatched = true; + count++; + } + } + } + + return dispatched; + } +} diff --git a/src/runtime/core/services/broadcast_channel.hh b/src/runtime/core/services/broadcast_channel.hh new file mode 100644 index 0000000000..a301dd7048 --- /dev/null +++ b/src/runtime/core/services/broadcast_channel.hh @@ -0,0 +1,65 @@ +#ifndef SOCKET_RUNTIME_CORE_SERVICES_BROADCAST_CHANNEL_H +#define SOCKET_RUNTIME_CORE_SERVICES_BROADCAST_CHANNEL_H + +#include "../../core.hh" +#include "../../unique_client.hh" + +namespace ssc::runtime::core::services { + class BroadcastChannel : public core::Service { + public: + using SubscriptionID = uint64_t; + using MessageEventID = uint64_t; + using MessageID = uint64_t; + using Client = UniqueClient; + + struct Subscription; + + struct Message { + const Client client; + const String origin = ""; + const String token = ""; + const String name = ""; + JSON::Any data; + const MessageID id = rand64(); + + const JSON::Object json () const; + }; + + struct MessageEvent : public Message { + const Subscription& subscription; + const MessageEventID id = rand64(); + const JSON::Object json () const; + }; + + using Observer = Observer; + using Observers = Observers; + using ObserversMap = Map; + using SubscriptionObservers = Map; + + struct Subscription { + const String name = ""; + const String origin = ""; + const Client client; + const Observer::Callback callback; + const SubscriptionID id = rand64(); + const JSON::Object json () const; + const String key () const; + }; + + using Subscriptions = Map; + + Mutex mutex; + ObserversMap observers; + Subscriptions subscriptions; + SubscriptionObservers subscriptionObservers; + + BroadcastChannel (const Options& options) + : core::Service(options) + {} + + const Subscription& subscribe (const Subscription& subscription); + bool unsubscribe (const SubscriptionID id); + bool postMessage (const Message& message); + }; +} +#endif diff --git a/src/core/modules/conduit.cc b/src/runtime/core/services/conduit.cc similarity index 66% rename from src/core/modules/conduit.cc rename to src/runtime/core/services/conduit.cc index 957b374365..5bbf8795a2 100644 --- a/src/core/modules/conduit.cc +++ b/src/runtime/core/services/conduit.cc @@ -1,27 +1,129 @@ -#include "../../app/app.hh" -#include "../core.hh" -#include "../codec.hh" +#include + +#include "../../runtime.hh" +#include "../../crypto.hh" +#include "../../config.hh" +#include "../../string.hh" +#include "../../bytes.hh" +#include "../../http.hh" +#include "../../env.hh" +#include "../../ipc.hh" +#include "../../url.hh" #include "conduit.hh" #define SHA_DIGEST_LENGTH 20 -namespace SSC { +using ssc::runtime::config::getUserConfig; +using ssc::runtime::string::toUpperCase; +using ssc::runtime::crypto::sha1; + +namespace ssc::runtime::core::services { static constexpr char WS_GUID[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; static SharedPointer vectorToSharedPointer (const Vector& vector) { const auto size = vector.size(); const auto data = vector.data(); const auto pointer = std::make_shared(size); - std::memcpy(pointer.get(), data, size); + memcpy(pointer.get(), data, size); return std::move(pointer); } - CoreConduit::CoreConduit (Core* core) : CoreModule(core) { - auto userConfig = getUserConfig(); - const auto sharedKey = Env::get( + inline String Conduit::Message::get (const String& key) const { + const auto it = options.find(key); + if (it != options.end()) { + return it->second; + } + return ""; + } + + inline bool Conduit::Message::has (const String& key) const { + const auto it = options.find(key); + if (it != options.end()) { + return true; + } + return false; + } + + inline String Conduit::Message::pluck (const String& key) { + auto it = options.find(key); + if (it != options.end()) { + String value = it->second; + options.erase(it); + return value; + } + return ""; + } + + inline Map Conduit::Message::map () const { + Map map; + for (const auto& entry : this->options) { + map.insert(entry); + } + return map; + } + + const inline bool Conduit::Message::empty () const { + return this->options.empty() && this->payload.size() == 0; + } + + void Conduit::Message::clear () { + this->options.clear(); + this->payload.clear(); + } + + inline const size_t Conduit::FrameBuffer::size () const { + return this->vector.size(); + } + + inline const unsigned char* Conduit::FrameBuffer::data () const { + return this->vector.data(); + } + + inline void Conduit::FrameBuffer::resize (const size_t size) { + this->vector.resize(size); + } + + unsigned char Conduit::FrameBuffer::operator [] (const unsigned int index) const { + if (index >= this->size()) { + return 0; + } + + return this->vector.at(index); + } + + unsigned char& Conduit::FrameBuffer::operator [] (const unsigned int index) { + if (index >= this->size()) { + this->vector.resize(index + 1); + } + + return this->vector.at(index); + } + + const Vector Conduit::FrameBuffer::slice ( + Vector::const_iterator& begin, + Vector::const_iterator& end + ) { + return Vector(begin, end); + } + + template + const Vector Conduit::FrameBuffer::slice ( + Vector::size_type start, + Vector::size_type end + ) { + return Vector(this->vector.begin() + start, this->vector.begin() + end); + } + + Conduit::Conduit (const Service::Options& options) + : core::Service(options) + { + static const auto userConfig = getUserConfig(); + static const auto sharedKey = runtime::env::get( "SOCKET_RUNTIME_CONDUIT_SHARED_KEY", - userConfig["application_conduit_shared_key"] + userConfig.contains("application_conduit_shared_key") + ? userConfig.at("application_conduit_shared_key") + : "" ); if (sharedKey.size() >= 8) { @@ -31,12 +133,12 @@ namespace SSC { } } - CoreConduit::~CoreConduit () { + Conduit::~Conduit () noexcept { this->stop(); } - Vector CoreConduit::encodeMessage ( - const CoreConduit::Options& options, + Vector Conduit::encodeMessage ( + const Conduit::Message::Options& options, const Vector& payload ) { Vector encodedMessage; @@ -76,10 +178,8 @@ namespace SSC { return encodedMessage; } - CoreConduit::DecodedMessage CoreConduit::decodeMessage ( - const Vector& data - ) { - DecodedMessage message; + Conduit::Message Conduit::decodeMessage (const Vector& data) { + Message message; if (data.size() < 1) return message; @@ -126,12 +226,12 @@ namespace SSC { return message; } - bool CoreConduit::has (uint64_t id) { + bool Conduit::has (uint64_t id) { Lock lock(this->mutex); return this->clients.find(id) != this->clients.end(); } - CoreConduit::Client::~Client () { + Conduit::Client::~Client () { auto handle = reinterpret_cast(&this->handle); if ( @@ -145,7 +245,7 @@ namespace SSC { } } - CoreConduit::Client* CoreConduit::get (uint64_t id) { + Conduit::Client* Conduit::get (uint64_t id) { Lock lock(this->mutex); const auto it = clients.find(id); @@ -156,47 +256,33 @@ namespace SSC { return nullptr; } - void CoreConduit::handshake ( - CoreConduit::Client* client, + void Conduit::handshake ( + Conduit::Client* client, const char* buffer ) { - const auto request = String(buffer); - const auto crlf = request.find("\r\n"); + const auto request = http::Request(buffer); - if (crlf == String::npos) { + if (!request.valid()) { // TODO(@jwerle); handle malformed handshake request return; } - String method; - String uri; - String version; - - auto inputStream = std::istringstream(request.substr(0, crlf)); - inputStream - >> method - >> uri - >> version; - - const auto url = URL(uri); - const auto headers = Headers(request); - const auto webSocketKey = headers.get("Sec-WebSocket-Key"); - const auto sharedKey = url.searchParams.get("key"); + const auto webSocketKey = request.headers.get("sec-websocket-key"); if (webSocketKey.empty()) { // debug("Sec-WebSocket-Key is required but missing."); return; } - if (url.pathComponents.size() >= 2) { + if (request.url.pathComponents.size() >= 2) { try { - client->id = url.pathComponents.get(0); + client->id = request.url.pathComponents.get(0); } catch (...) { // debug("Unable to parse socket id"); } try { - client->clientId = url.pathComponents.get(1); + client->clientId = request.url.pathComponents.get(1); } catch (...) { // debug("Unable to parse client id"); } @@ -220,30 +306,23 @@ namespace SSC { } while (0); if (sharedKey != this->sharedKey) { - debug("CoreConduit::Client failed auth"); + debug("Conduit::Client failed auth"); client->close(); return; } - // debug("Received key: %s", webSocketKey.c_str()); - - const auto acceptKey = webSocketKey + WS_GUID; - const auto acceptKeyHash = shacalc(acceptKey); - const auto encodedAcceptKeyHash = encodeBase64(acceptKeyHash); - - // debug("Generated Accept Key: %s\n", base64_accept_key); // Debugging statement - - StringStream oss; - oss - << "HTTP/1.1 101 Switching Protocols\r\n" - << "Upgrade: websocket\r\n" - << "Connection: Upgrade\r\n" - << "Sec-WebSocket-Accept: " << encodedAcceptKeyHash.data() << "\r\n\r\n"; + const auto response = http::Response(101) + .setHeader("upgrade", "websocket") + .setHeader("connection", "upgrade") + .setHeader( + "sec-websocket-accept", + bytes::base64::encode(crypto::sha1(webSocketKey + WS_GUID)) + ); - const auto response = oss.str(); - const auto size = response.size(); + const auto output = response.str(); + const auto size = output.size(); const auto data = new char[size]{0}; - memcpy(data, response.c_str(), size); + memcpy(data, output.c_str(), size); const auto buf = uv_buf_init(data, size); @@ -267,11 +346,12 @@ namespace SSC { client->isHandshakeDone = 1; } - void CoreConduit::processFrame ( + void Conduit::processFrame ( Client* client, const char* frame, ssize_t len ) { + Lock lock(client->mutex); if (len < 2) return; // Frame too short to be valid unsigned char *data = (unsigned char *)frame; @@ -306,28 +386,28 @@ namespace SSC { memcpy(maskingKey, data + pos, 4); pos += 4; - if (client->frameBuffer.size() == 0) { - client->frameBuffer.resize(payloadSize + (2 * 1024 * 1024)); - } - // resize client frame buffer if payload size is too big to fit - if (payloadSize + client->frameBufferOffset > client->frameBuffer.size()) { - client->frameBuffer.resize(payloadSize + client->frameBufferOffset); + if (payloadSize > client->frameBuffer.size()) { + client->frameBuffer.resize(payloadSize + 1); } for (uint64_t i = 0; i < payloadSize; ++i) { - client->frameBuffer[client->frameBufferOffset + i] = data[pos + i] ^ maskingKey[i % 4]; + client->frameBuffer[i] = data[pos + i] ^ maskingKey[i % 4]; } auto decoded = this->decodeMessage(client->frameBuffer.slice( - client->frameBufferOffset, - client->frameBufferOffset + payloadSize + 0, + payloadSize )); - pos += payloadSize; - client->frameBufferOffset += payloadSize; - client->queue.push_back(decoded); + if (decoded.has("digest")) { + const auto bytes = vectorToSharedPointer(decoded.payload); + const auto inputDigest = toUpperCase(decoded.get("digest")); + const auto computedDigest = toUpperCase(sha1(bytes, decoded.payload.size())); + client->send({{"digest", computedDigest}}, nullptr, 0); + return; + } if (!decoded.has("route")) { if (decoded.has("to")) { @@ -335,7 +415,6 @@ namespace SSC { const auto from = client->id; const auto to = std::stoull(decoded.get("to")); if (to != from) { - const auto app = App::sharedApplication(); const auto options = decoded.options; size_t size = 0; size_t offset = 0; @@ -356,17 +435,20 @@ namespace SSC { const auto payload = std::make_shared(size); memcpy(payload.get(), bytes.get(), size); - app->dispatch([this, options, size, payload, from, to] () { + this->dispatch([this, options, size, payload, from, to] () { Lock lock(this->mutex); auto recipient = this->clients[to]; auto client = this->clients[from]; if (client != nullptr && recipient != nullptr) { - recipient->send(options, payload, size); + Lock lock(client->mutex); + do { + Lock lock(recipient->mutex); + recipient->send(options, payload, size); + } while (0); } }); } } catch (...) { - debug("Invalid 'to' parameter in encoded message"); } } @@ -374,31 +456,24 @@ namespace SSC { return; } - /* const auto uri = URL::Builder() + const auto uri = URL::Builder() .setProtocol("ipc") .setHostname(decoded.pluck("route")) .setSearchParam("id", client->id) - .setSearchParams(decoded.getOptionsAsMap()) - .build(); */ - std::stringstream ss; + .setSearchParams(decoded.map()) + .build() + .str(); - ss << "ipc://"; - ss << decoded.pluck("route"); - ss << "/?id=" << std::to_string(client->id); - - for (auto& option : decoded.getOptionsAsMap()) { - auto key = option.first; - auto value = option.second == "value" ? encodeURIComponent(option.second) : option.second; - ss << "&" << key << "=" << value; - } - - size_t size = 0; size_t offset = 0; Vector buffer; + + size_t size = 0; for (const auto& entry : client->queue) { size += entry.payload.size(); } + buffer.resize(size); + for (const auto& entry : client->queue) { for (int i = 0; i < entry.payload.size(); ++i) { buffer[offset + i] = entry.payload[i]; @@ -407,61 +482,80 @@ namespace SSC { offset += entry.payload.size(); } + client->queue.clear(); + const auto bytes = vectorToSharedPointer(buffer); - const auto app = App::sharedApplication(); - const auto uri = ss.str(); + const auto message = ipc::Message(uri); auto window = client && client->clientId > 0 - ? app->windowManager.getWindowForClient({ .id = client->clientId }) + ? this->context.getRuntime()->windowManager.getWindowForClient({ client->clientId }) : nullptr; - client->queue.clear(); - client->frameBufferOffset = 0; - - if (window != nullptr) { - app->dispatch([window, uri, bytes, size]() { - const auto invoked = window->bridge.router.invoke( - uri, - bytes, - size - ); - - if (!invoked) { - // TODO(@jwerle,@heapwolf): handle this - // debug("there was a problem invoking the router %s", ss.str().c_str()); + // prevent external usage of internal routes + if (message.name.starts_with("internal.")) { + const auto result = ipc::Result(ipc::Result::Err { + message, + JSON::Object::Entries { + {"type", "NotFoundError"}, + {"message", "Not found"} } }); + const auto data = result.json().str(); + const auto size = data.size(); + const auto payload = std::make_shared(size); + memcpy(payload.get(), data.c_str(), size); + client->send({}, payload, size); + return; + } + + bool invoked = false; + if (window != nullptr) { + invoked = window->bridge->router.invoke(uri, bytes, size); } else { - window = app->windowManager.getWindow(0); - app->dispatch([window, uri, client, bytes, size]() { - const auto invoked = window->bridge.router.invoke( - uri, - bytes, - size, - [client](const auto result) { - auto token = result.token; - if (result.post.body != nullptr && result.post.length > 0) { - client->send({{"token", token}}, result.post.body, result.post.length); - } else { - const auto data = result.json().str(); - const auto size = data.size(); - const auto payload = std::make_shared(size); - memcpy(payload.get(), data.c_str(), size); - client->send({{"token", token}}, payload, size); - } + window = this->context.getRuntime()->windowManager.getWindow(0); + invoked = window->bridge->router.invoke( + uri, + bytes, + size, + [client](const auto result) { + auto token = result.token; + if (result.queuedResponse.body != nullptr && result.queuedResponse.length > 0) { + client->send({{"token", token}}, result.queuedResponse.body, result.queuedResponse.length); + } else { + const auto data = result.json().str(); + const auto size = data.size(); + const auto payload = std::make_shared(size); + memcpy(payload.get(), data.c_str(), size); + client->send({{"token", token}}, payload, size); } - ); + } + ); + } + + if (!invoked) { + const auto result = ipc::Result(ipc::Result::Err { + message, + JSON::Object::Entries { + {"type", "NotFoundError"}, + {"message", "Not found"} + } }); + const auto data = result.json().str(); + const auto size = data.size(); + const auto payload = std::make_shared(size); + memcpy(payload.get(), data.c_str(), size); + client->send({}, payload, size); + return; } } struct ClientWriteContext { - CoreConduit::Client* client = nullptr; + Conduit::Client* client = nullptr; const Function callback = nullptr; }; - bool CoreConduit::Client::send ( - const CoreConduit::Options& options, + bool Conduit::Client::send ( + const Conduit::Message::Options& options, SharedPointer bytes, size_t length, int opcode, @@ -479,11 +573,17 @@ namespace SSC { try { encodedMessage = this->conduit->encodeMessage(options, payload); } catch (const std::exception& e) { - debug("CoreConduit::Client: Error - Failed to encode message payload: %s", e.what()); + debug("Conduit::Client: Error - Failed to encode message payload: %s", e.what()); return false; } - this->conduit->core->dispatchEventLoop([this, opcode, callback, handle, encodedMessage = std::move(encodedMessage)]() mutable { + this->conduit->loop.dispatch([ + this, + opcode, + handle, + callback = std::move(callback), + encodedMessage = std::move(encodedMessage) + ]() mutable { size_t encodedLength = encodedMessage.size(); Vector frame; @@ -536,7 +636,7 @@ namespace SSC { delete req; if (context != nullptr) { - context->client->conduit->core->dispatchEventLoop([=]() mutable { + context->client->conduit->loop.dispatch([=]() mutable { context->callback(); delete context; }); @@ -549,16 +649,16 @@ namespace SSC { } struct ClientCloseContext { - CoreConduit::Client* client = nullptr; - CoreConduit::Client::CloseCallback callback = nullptr; + Conduit::Client* client = nullptr; + Conduit::Client::CloseCallback callback = nullptr; }; - void CoreConduit::Client::close (const CloseCallback& callback) { + void Conduit::Client::close (const CloseCallback callback) { auto handle = reinterpret_cast(&this->handle); if (this->isClosing || this->isClosed || !uv_is_active(handle)) { if (callback != nullptr) { - this->conduit->core->dispatchEventLoop(callback); + this->conduit->loop.dispatch(callback); } return; } @@ -581,7 +681,7 @@ namespace SSC { } if (callback != nullptr) { - this->conduit->core->dispatchEventLoop(callback); + this->conduit->loop.dispatch(callback); } return; } @@ -592,7 +692,7 @@ namespace SSC { this->isClosing = false; if (callback != nullptr) { - this->conduit->core->dispatchEventLoop(callback); + this->conduit->loop.dispatch(callback); } return; } @@ -634,7 +734,7 @@ namespace SSC { delete context; if (callback != nullptr) { - client->conduit->core->dispatchEventLoop(callback); + client->conduit->loop.dispatch(callback); } }); }); @@ -643,29 +743,31 @@ namespace SSC { this->send({}, vectorToSharedPointer({ 0x00 }), 1, 0x08, closeHandle); } - void CoreConduit::start (const StartCallback& callback) { + bool Conduit::start () { + return this->start(nullptr); + } + + bool Conduit::start (const StartCallback callback) { if (this->isActive() || this->isStarting) { if (callback != nullptr) { - this->core->dispatchEventLoop(callback); + this->loop.dispatch(callback); } - return; + return true; } - auto loop = this->core->getEventLoop(); - this->isStarting = true; + this->hostname = runtime::env::get("SOCKET_RUNTIME_CONDUIT_HOSTNAME", this->hostname); - this->hostname = Env::get("SOCKET_RUNTIME_CONDUIT_HOSTNAME", this->hostname); auto port = this->port.load(); - if (Env::has("SOCKET_RUNTIME_CONDUIT_PORT")) { + if (runtime::env::has("SOCKET_RUNTIME_CONDUIT_PORT")) { try { - port = std::stoi(Env::get("SOCKET_RUNTIME_CONDUIT_PORT")); + port = std::stoi(runtime::env::get("SOCKET_RUNTIME_CONDUIT_PORT")); } catch (...) {} } uv_ip4_addr(this->hostname.c_str(), port, &this->addr); - uv_tcp_init(loop, &this->socket); + uv_tcp_init(loop.get(), &this->socket); uv_tcp_bind( &this->socket, reinterpret_cast(&this->addr), @@ -688,13 +790,12 @@ namespace SSC { this->port = ntohs(sockname.sin_port); const auto result = uv_listen(reinterpret_cast(&this->socket), 128, [](uv_stream_t* stream, int status) { if (status < 0) { - // debug("New connection error %s\n", uv_strerror(status)); return; } auto data = uv_handle_get_data(reinterpret_cast(stream)); - auto conduit = static_cast(data); - auto client = new CoreConduit::Client(conduit); + auto conduit = static_cast(data); + auto client = new Conduit::Client(conduit); auto loop = uv_handle_get_loop(reinterpret_cast(stream)); uv_tcp_init( @@ -722,20 +823,26 @@ namespace SSC { uv_read_start( reinterpret_cast(&client->handle), [](uv_handle_t* handle, size_t size, uv_buf_t* buf) { - buf->base = new char[size]{0}; - buf->len = size; + if (buf && size > 0) { + buf->base = new char[size]{0}; + buf->len = size; + } else if (buf) { + buf->base = nullptr; + buf->len = 0; + } }, [](uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { auto data = uv_handle_get_data(reinterpret_cast(stream)); - auto client = static_cast(data); + auto client = static_cast(data); + auto buffer = buf->base; if (client && !client->isClosing && !client->isClosed && nread > 0) { if (client->isHandshakeDone) { do { Lock lock(client->conduit->mutex); if (!client->conduit->clients.contains(client->id)) { - if (buf->base) { - delete [] buf->base; + if (buffer) { + delete [] buffer; } client->close([client]() { if (client->isClosed) { @@ -745,10 +852,9 @@ namespace SSC { return; } } while (0); - - client->conduit->processFrame(client, buf->base, nread); + client->conduit->processFrame(client, buffer, nread); } else { - client->conduit->handshake(client, buf->base); + client->conduit->handshake(client, buffer); } } else if (nread < 0) { if (nread != UV_EOF) { @@ -764,30 +870,32 @@ namespace SSC { } } - if (buf->base) { - delete [] buf->base; + if (buffer) { + delete [] buffer; } } ); }); if (result) { - debug("CoreConduit: Listen error %s\n", uv_strerror(result)); + debug("Conduit: Listen error %s\n", uv_strerror(result)); } this->isStarting = false; if (callback != nullptr) { - this->core->dispatchEventLoop(callback); + this->loop.dispatch(callback); } + + return result == 0; } - void CoreConduit::stop () { + bool Conduit::stop () { if (!this->isActive()) { - return; + return false; } - this->core->dispatchEventLoop([this]() { + return this->loop.dispatch([this]() { Lock lock(this->mutex); auto handle = reinterpret_cast(&this->socket); const auto closeHandle = [=, this] () { @@ -803,11 +911,11 @@ namespace SSC { reinterpret_cast(&this->socket), [](uv_shutdown_t* shutdown, int status) { auto data = uv_handle_get_data(reinterpret_cast(shutdown)); - auto conduit = reinterpret_cast(data); + auto conduit = reinterpret_cast(data); delete shutdown; - conduit->core->dispatchEventLoop([=]() { + conduit->loop.dispatch([=]() { uv_close( reinterpret_cast(&conduit->socket), nullptr @@ -847,7 +955,7 @@ namespace SSC { }); } - bool CoreConduit::isActive () { + bool Conduit::isActive () { Lock lock(this->mutex); return ( this->port > 0 && diff --git a/src/runtime/core/services/conduit.hh b/src/runtime/core/services/conduit.hh new file mode 100644 index 0000000000..a175e9ef95 --- /dev/null +++ b/src/runtime/core/services/conduit.hh @@ -0,0 +1,110 @@ +#ifndef SOCKET_RUNTIME_CORE_SERVICES_CONDUIT_H +#define SOCKET_RUNTIME_CORE_SERVICES_CONDUIT_H + +#include "../../core.hh" +#include "../../ipc.hh" + +namespace ssc::runtime::core::services { + class Conduit : public core::Service { + public: + using StartCallback = Function; + + struct Message { + using Options = UnorderedMap; + Options options; + Vector payload; + + inline String get (const String& key) const; + inline bool has (const String& key) const; + inline String pluck (const String& key); + inline Map map () const; + const inline bool empty () const; + void clear (); + }; + + struct FrameBuffer { + Vector vector; + + inline const size_t size () const; + inline const unsigned char* data () const; + inline void resize (const size_t size); + + unsigned char operator [] (const unsigned int index) const; + unsigned char& operator [] (const unsigned int index); + + const Vector slice (Vector::const_iterator&, Vector::const_iterator&); + template + const Vector slice (Vector::size_type, Vector::size_type); + }; + + class Client { + public: + using CloseCallback = Function; + using SendCallback = Function; + using ID = uint64_t; + + ID id = 0; + // client state + ID clientId = 0; + Atomic isHandshakeDone = false; + Atomic isClosing = false; + Atomic isClosed = false; + Mutex mutex; + + // uv state + uv_tcp_t handle; + uv_buf_t buffer; + uv_stream_t* stream = nullptr; + + // websocket frame buffer state + FrameBuffer frameBuffer; + Vector queue; + Conduit* conduit = nullptr; + + Client (Conduit* conduit) + : conduit(conduit), + id(0), + clientId(0), + isHandshakeDone(0) + {} + + ~Client (); + + bool send ( const Message::Options&, SharedPointer, size_t, int opcode = 2, const SendCallback = nullptr); + void close (const CloseCallback callback = nullptr); + }; + + // state + std::map clients; + String sharedKey; + Atomic isStarting = false; + Atomic port = 0; + String hostname = "0.0.0.0"; + Mutex mutex; + + Conduit (const Service::Options& options); + ~Conduit () noexcept override; + + // codec + Message decodeMessage (const Vector& data); + Vector encodeMessage (const Message::Options&, const Vector&); + + // client access + bool has (uint64_t id); + Conduit::Client* get (uint64_t id); + + // lifecycle + bool start (const StartCallback callback); + bool start () override; + bool stop () override; + bool isActive (); + + private: + uv_tcp_t socket; + struct sockaddr_in addr; + + void handshake (Client*, const char*); + void processFrame (Client*, const char*, ssize_t); + }; +} +#endif diff --git a/src/core/modules/diagnostics.cc b/src/runtime/core/services/diagnostics.cc similarity index 52% rename from src/core/modules/diagnostics.cc rename to src/runtime/core/services/diagnostics.cc index 6e63024ade..0b5d68ad89 100644 --- a/src/core/modules/diagnostics.cc +++ b/src/runtime/core/services/diagnostics.cc @@ -1,8 +1,8 @@ -#include "../core.hh" #include "diagnostics.hh" +#include "../services.hh" -namespace SSC { - JSON::Object CoreDiagnostics::Diagnostic::Handles::json () const { +namespace ssc::runtime::core::services { + JSON::Object Diagnostics::Diagnostic::Handles::json () const { auto ids = JSON::Array {}; for (const auto id : this->ids) { ids.push(std::to_string(id)); @@ -13,25 +13,25 @@ namespace SSC { }; } - void CoreDiagnostics::query (const QueryCallback& callback) const { - this->core->dispatchEventLoop([=, this] () mutable { + void Diagnostics::query (const QueryCallback callback) const { + this->loop.dispatch([=, this] () mutable { auto query = QueryDiagnostic {}; - // posts diagnostics + // queued responses diagnostics do { - Lock lock(this->core->mutex); - query.posts.handles.count = this->core->posts.size(); - for (const auto& entry : this->core->posts) { - query.posts.handles.ids.push_back(entry.first); + Lock lock(this->services.mutex); + query.queuedResponses.handles.count = this->context.queuedResponses.size(); + for (const auto& entry : this->context.queuedResponses) { + query.queuedResponses.handles.ids.push_back(entry.first); } } while (0); #if !SOCKET_RUNTIME_PLATFORM_IOS // `childProcess` diagnostics do { - Lock lock(this->core->childProcess.mutex); - query.childProcess.handles.count = this->core->childProcess.handles.size(); - for (const auto& entry : this->core->childProcess.handles) { + Lock lock(this->services.process.mutex); + query.childProcess.handles.count = this->services.process.handles.size(); + for (const auto& entry : this->services.process.handles) { query.childProcess.handles.ids.push_back(entry.first); } } while (0); @@ -39,41 +39,41 @@ namespace SSC { // ai diagnostics do { - Lock lock(this->core->ai.mutex); - query.ai.llm.handles.count = this->core->ai.llms.size(); - for (const auto& entry : this->core->ai.llms) { + Lock lock(this->services.ai.mutex); + query.ai.llm.handles.count = this->services.ai.llms.size(); + for (const auto& entry : this->services.ai.llms) { query.ai.llm.handles.ids.push_back(entry.first); } } while (0); // fs diagnostics do { - Lock lock(this->core->fs.mutex); - query.fs.descriptors.handles.count = this->core->fs.descriptors.size(); - query.fs.watchers.handles.count = this->core->fs.watchers.size(); + Lock lock(this->services.fs.mutex); + query.fs.descriptors.handles.count = this->services.fs.descriptors.size(); + query.fs.watchers.handles.count = this->services.fs.watchers.size(); - for (const auto& entry : this->core->fs.descriptors) { + for (const auto& entry : this->services.fs.descriptors) { query.fs.descriptors.handles.ids.push_back(entry.first); } - for (const auto& entry : this->core->fs.watchers) { + for (const auto& entry : this->services.fs.watchers) { query.fs.watchers.handles.ids.push_back(entry.first); } } while (0); // timers diagnostics do { - Lock lock(this->core->timers.mutex); - for (const auto& entry : this->core->timers.handles) { + Lock lock(this->services.timers.mutex); + for (const auto& entry : this->services.timers.handles) { const auto id = entry.first; const auto& timer = entry.second; - if (timer->type == CoreTimers::Timer::Type::Timeout) { + if (timer->type == Timers::Timer::Type::Timeout) { query.timers.timeout.handles.count++; query.timers.timeout.handles.ids.push_back(entry.first); - } else if (timer->type == CoreTimers::Timer::Type::Interval) { + } else if (timer->type == Timers::Timer::Type::Interval) { query.timers.interval.handles.count++; query.timers.interval.handles.ids.push_back(entry.first); - } else if (timer->type == CoreTimers::Timer::Type::Immediate) { + } else if (timer->type == Timers::Timer::Type::Immediate) { query.timers.immediate.handles.count++; query.timers.immediate.handles.ids.push_back(entry.first); } @@ -82,52 +82,52 @@ namespace SSC { // udp do { - Lock lock(this->core->udp.mutex); - query.udp.handles.count = this->core->udp.sockets.size(); - for (const auto& entry : this->core->udp.sockets) { + Lock lock(this->services.udp.mutex); + query.udp.handles.count = this->services.udp.manager.sockets.size(); + for (const auto& entry : this->services.udp.manager.sockets) { query.udp.handles.ids.push_back(entry.first); } } while (0); // conduit do { - Lock lock(this->core->conduit.mutex); - query.conduit.handles.count = this->core->conduit.clients.size(); - query.conduit.isActive = this->core->conduit.isActive(); - for (const auto& entry : this->core->conduit.clients) { + Lock lock(this->services.conduit.mutex); + query.conduit.handles.count = this->services.conduit.clients.size(); + query.conduit.isActive = this->services.conduit.isActive(); + for (const auto& entry : this->services.conduit.clients) { query.conduit.handles.ids.push_back(entry.first); } } while (0); // uv do { - Lock lock(this->core->mutex); - uv_metrics_info(&this->core->eventLoop, &query.uv.metrics); - query.uv.idleTime = uv_metrics_idle_time(&this->core->eventLoop); - query.uv.handles.count = this->core->eventLoop.active_handles; - query.uv.activeRequests = this->core->eventLoop.active_reqs.count; + Lock lock(this->loop.mutex); + uv_metrics_info(this->loop.get(), &query.uv.metrics); + query.uv.idleTime = uv_metrics_idle_time(this->loop.get()); + query.uv.handles.count = this->loop.get()->active_handles; + query.uv.activeRequests = this->loop.get()->active_reqs.count; } while (0); callback(query); }); } - void CoreDiagnostics::query ( + void Diagnostics::query ( const String& seq, - const CoreModule::Callback& callback + const Callback callback ) const { - this->core->dispatchEventLoop([=, this] () { + this->loop.dispatch([=, this] () { this->query([=] (const auto query) { auto json = JSON::Object::Entries { {"source", "diagnostics.query"}, {"data", query.json()} }; - callback(seq, json, Post {}); + callback(seq, json, QueuedResponse {}); }); }); } - JSON::Object CoreDiagnostics::UVDiagnostic::json () const { + JSON::Object Diagnostics::UVDiagnostic::json () const { return JSON::Object::Entries { {"metrics", JSON::Object::Entries { {"loopCount", this->metrics.loop_count}, @@ -140,50 +140,50 @@ namespace SSC { }; } - JSON::Object CoreDiagnostics::PostsDiagnostic::json () const { + JSON::Object Diagnostics::QueuedResponsesDiagnostic::json () const { return JSON::Object::Entries { {"handles", this->handles.json()} }; } - JSON::Object CoreDiagnostics::ChildProcessDiagnostic::json () const { + JSON::Object Diagnostics::ChildProcessDiagnostic::json () const { return JSON::Object::Entries { {"handles", this->handles.json()} }; } - JSON::Object CoreDiagnostics::AIDiagnostic::json () const { + JSON::Object Diagnostics::AIDiagnostic::json () const { return JSON::Object::Entries { {"llm", this->llm.json()} }; } - JSON::Object CoreDiagnostics::AIDiagnostic::LLMDiagnostic::json () const { + JSON::Object Diagnostics::AIDiagnostic::LLMDiagnostic::json () const { return JSON::Object::Entries { {"handles", this->handles.json()} }; } - JSON::Object CoreDiagnostics::FSDiagnostic::json () const { + JSON::Object Diagnostics::FSDiagnostic::json () const { return JSON::Object::Entries { {"watchers", this->watchers.json()}, {"descriptors", this->descriptors.json()} }; } - JSON::Object CoreDiagnostics::FSDiagnostic::WatchersDiagnostic::json () const { + JSON::Object Diagnostics::FSDiagnostic::WatchersDiagnostic::json () const { return JSON::Object::Entries { {"handles", this->handles.json()} }; } - JSON::Object CoreDiagnostics::FSDiagnostic::DescriptorsDiagnostic::json () const { + JSON::Object Diagnostics::FSDiagnostic::DescriptorsDiagnostic::json () const { return JSON::Object::Entries { {"handles", this->handles.json()} }; } - JSON::Object CoreDiagnostics::TimersDiagnostic::json () const { + JSON::Object Diagnostics::TimersDiagnostic::json () const { return JSON::Object::Entries { {"timeout", this->timeout.json()}, {"interval", this->interval.json()}, @@ -191,40 +191,40 @@ namespace SSC { }; } - JSON::Object CoreDiagnostics::TimersDiagnostic::TimeoutDiagnostic::json () const { + JSON::Object Diagnostics::TimersDiagnostic::TimeoutDiagnostic::json () const { return JSON::Object::Entries { {"handles", this->handles.json()} }; } - JSON::Object CoreDiagnostics::TimersDiagnostic::IntervalDiagnostic::json () const { + JSON::Object Diagnostics::TimersDiagnostic::IntervalDiagnostic::json () const { return JSON::Object::Entries { {"handles", this->handles.json()} }; } - JSON::Object CoreDiagnostics::TimersDiagnostic::ImmediateDiagnostic::json () const { + JSON::Object Diagnostics::TimersDiagnostic::ImmediateDiagnostic::json () const { return JSON::Object::Entries { {"handles", this->handles.json()} }; } - JSON::Object CoreDiagnostics::UDPDiagnostic::json () const { + JSON::Object Diagnostics::UDPDiagnostic::json () const { return JSON::Object::Entries { {"handles", this->handles.json()} }; } - JSON::Object CoreDiagnostics::ConduitDiagnostic::json () const { + JSON::Object Diagnostics::ConduitDiagnostic::json () const { return JSON::Object::Entries { {"handles", this->handles.json()}, {"isActive", this->isActive} }; } - JSON::Object CoreDiagnostics::QueryDiagnostic::json () const { + JSON::Object Diagnostics::QueryDiagnostic::json () const { return JSON::Object::Entries { - {"posts", this->posts.json()}, + {"queuedResponses", this->queuedResponses.json()}, {"childProcess", this->childProcess.json()}, {"ai", this->ai.json()}, {"fs", this->fs.json()}, diff --git a/src/core/modules/diagnostics.hh b/src/runtime/core/services/diagnostics.hh similarity index 82% rename from src/core/modules/diagnostics.hh rename to src/runtime/core/services/diagnostics.hh index 0e63cd9ef2..259eee879c 100644 --- a/src/core/modules/diagnostics.hh +++ b/src/runtime/core/services/diagnostics.hh @@ -1,12 +1,11 @@ -#ifndef SOCKET_RUNTIME_CORE_MODULE_DIAGNOSTICS_H -#define SOCKET_RUNTIME_CORE_MODULE_DIAGNOSTICS_H +#ifndef SOCKET_RUNTIME_CORE_SERVICES_DIAGNOSTICS_H +#define SOCKET_RUNTIME_CORE_SERVICES_DIAGNOSTICS_H -#include "../json.hh" -#include "../module.hh" +#include "../../core.hh" +#include "../../ipc.hh" -namespace SSC { - class Core; - class CoreDiagnostics : public CoreModule { +namespace ssc::runtime::core::services { + class Diagnostics : public core::Service { public: struct Diagnostic { using ID = uint64_t; @@ -27,7 +26,7 @@ namespace SSC { JSON::Object json () const override; }; - struct PostsDiagnostic : public Diagnostic { + struct QueuedResponsesDiagnostic : public Diagnostic { Handles handles; JSON::Object json () const override; }; @@ -99,7 +98,7 @@ namespace SSC { }; struct QueryDiagnostic : public Diagnostic { - PostsDiagnostic posts; + QueuedResponsesDiagnostic queuedResponses; ChildProcessDiagnostic childProcess; AIDiagnostic ai; FSDiagnostic fs; @@ -111,17 +110,14 @@ namespace SSC { JSON::Object json () const override; }; - using QueryCallback = Function; + using QueryCallback = Function; - CoreDiagnostics (Core* core) - : CoreModule(core) + Diagnostics (const Options& options) + : core::Service(options) {} - void query (const QueryCallback& callback) const; - void query ( - const String& seq, - const CoreModule::Callback& callback - ) const; + void query (const QueryCallback) const; + void query (const ipc::Message::Seq&, const Callback) const; }; } #endif diff --git a/src/core/modules/dns.cc b/src/runtime/core/services/dns.cc similarity index 62% rename from src/core/modules/dns.cc rename to src/runtime/core/services/dns.cc index 18fdc7d470..59233bcb57 100644 --- a/src/core/modules/dns.cc +++ b/src/runtime/core/services/dns.cc @@ -1,21 +1,22 @@ -#include "../core.hh" #include "dns.hh" -namespace SSC { - void CoreDNS::lookup ( +namespace ssc::runtime::core::services { + void DNS::lookup ( const String& seq, const LookupOptions& options, - const CoreModule::Callback& callback + const Callback callback ) const { - this->core->dispatchEventLoop([=, this]() { - auto ctx = new CoreModule::RequestContext(seq, callback); - auto loop = this->core->getEventLoop(); + const auto family = options.family; + const auto hostname = options.hostname; + this->loop.dispatch([this, seq, callback, family, hostname]() { + const auto ctx = new RequestContext {seq, callback}; + auto loop = this->loop.get(); struct addrinfo hints = {0}; - if (options.family == 6) { + if (family == 6) { hints.ai_family = AF_INET6; - } else if (options.family == 4) { + } else if (family == 4) { hints.ai_family = AF_INET; } else { hints.ai_family = AF_UNSPEC; @@ -24,14 +25,14 @@ namespace SSC { hints.ai_socktype = 0; // `0` for any hints.ai_protocol = 0; // `0` for any - auto resolver = new uv_getaddrinfo_t; + const auto resolver = new uv_getaddrinfo_t; resolver->data = ctx; - auto err = uv_getaddrinfo(loop, resolver, [](uv_getaddrinfo_t *resolver, int status, struct addrinfo *res) { - auto ctx = (RequestContext*) resolver->data; + const auto err = uv_getaddrinfo(loop, resolver, [](uv_getaddrinfo_t *resolver, int status, struct addrinfo *res) { + const auto ctx = (RequestContext*) resolver->data; if (status < 0) { - auto result = JSON::Object::Entries { + const auto result = JSON::Object::Entries { {"source", "dns.lookup"}, {"err", JSON::Object::Entries { {"code", std::to_string(status)}, @@ -39,7 +40,7 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, result, Post{}); + ctx->callback(ctx->seq, result, QueuedResponse{}); uv_freeaddrinfo(res); delete resolver; delete ctx; @@ -60,13 +61,13 @@ namespace SSC { address = address.erase(address.find('\0')); - auto family = res->ai_family == AF_INET + const auto family = res->ai_family == AF_INET ? 4 : res->ai_family == AF_INET6 ? 6 : 0; - auto result = JSON::Object::Entries { + const auto result = JSON::Object::Entries { {"source", "dns.lookup"}, {"data", JSON::Object::Entries { {"address", address}, @@ -74,14 +75,14 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, result, Post{}); + ctx->callback(ctx->seq, result, QueuedResponse{}); uv_freeaddrinfo(res); delete resolver; delete ctx; - }, options.hostname.c_str(), nullptr, &hints); + }, hostname.c_str(), nullptr, &hints); if (err < 0) { - auto result = JSON::Object::Entries { + const auto result = JSON::Object::Entries { {"source", "dns.lookup"}, {"err", JSON::Object::Entries { {"code", std::to_string(err)}, @@ -89,7 +90,7 @@ namespace SSC { }} }; - ctx->callback(seq, result, Post{}); + ctx->callback(seq, result, QueuedResponse{}); delete ctx; } }); diff --git a/src/runtime/core/services/dns.hh b/src/runtime/core/services/dns.hh new file mode 100644 index 0000000000..0476b6d37f --- /dev/null +++ b/src/runtime/core/services/dns.hh @@ -0,0 +1,22 @@ +#ifndef SOCKET_RUNTIME_CORE_SERVICES_DNS_H +#define SOCKET_RUNTIME_CORE_SERVICES_DNS_H + +#include "../../core.hh" + +namespace ssc::runtime::core::services { + class DNS : public core::Service { + public: + struct LookupOptions { + String hostname; + int family; + // TODO: support these options: hints, all, verbatim + }; + + DNS (const Options& options) + : core::Service(options) + {} + + void lookup (const String&, const LookupOptions&, const Callback) const; + }; +} +#endif diff --git a/src/core/modules/fs.cc b/src/runtime/core/services/fs.cc similarity index 82% rename from src/core/modules/fs.cc rename to src/runtime/core/services/fs.cc index cf72b0de7c..e93af227dc 100644 --- a/src/core/modules/fs.cc +++ b/src/runtime/core/services/fs.cc @@ -1,179 +1,15 @@ -#include "../core.hh" -#include "../headers.hh" -#include "../json.hh" -#include "../resource.hh" -#include "../trace.hh" +#include "../../string.hh" +#include "../../http.hh" +#include "../../json.hh" +#include "../../url.hh" #include "fs.hh" -namespace SSC { - #define CONSTANT(c) { #c, (c) }, - static const std::map FS_CONSTANTS = { - #if defined(UV_DIRENT_UNKNOWN) - CONSTANT(UV_DIRENT_UNKNOWN) - #endif - #if defined(UV_DIRENT_FILE) - CONSTANT(UV_DIRENT_FILE) - #endif - #if defined(UV_DIRENT_DIR) - CONSTANT(UV_DIRENT_DIR) - #endif - #if defined(UV_DIRENT_LINK) - CONSTANT(UV_DIRENT_LINK) - #endif - #if defined(UV_DIRENT_FIFO) - CONSTANT(UV_DIRENT_FIFO) - #endif - #if defined(UV_DIRENT_SOCKET) - CONSTANT(UV_DIRENT_SOCKET) - #endif - #if defined(UV_DIRENT_CHAR) - CONSTANT(UV_DIRENT_CHAR) - #endif - #if defined(UV_DIRENT_BLOCK) - CONSTANT(UV_DIRENT_BLOCK) - #endif - #if defined(UV_FS_O_FILEMAP) - CONSTANT(UV_FS_O_FILEMAP) - #endif - #if defined(O_RDONLY) - CONSTANT(O_RDONLY) - #endif - #if defined(O_WRONLY) - CONSTANT(O_WRONLY) - #endif - #if defined(O_RDWR) - CONSTANT(O_RDWR) - #endif - #if defined(O_APPEND) - CONSTANT(O_APPEND) - #endif - #if defined(O_ASYNC) - CONSTANT(O_ASYNC) - #endif - #if defined(O_CLOEXEC) - CONSTANT(O_CLOEXEC) - #endif - #if defined(O_CREAT) - CONSTANT(O_CREAT) - #endif - #if defined(O_DIRECT) - CONSTANT(O_DIRECT) - #endif - #if defined(O_DIRECTORY) - CONSTANT(O_DIRECTORY) - #endif - #if defined(O_DSYNC) - CONSTANT(O_DSYNC) - #endif - #if defined(O_EXCL) - CONSTANT(O_EXCL) - #endif - #if defined(O_LARGEFILE) - CONSTANT(O_LARGEFILE) - #endif - #if defined(O_NOATIME) - CONSTANT(O_NOATIME) - #endif - #if defined(O_NOCTTY) - CONSTANT(O_NOCTTY) - #endif - #if defined(O_NOFOLLOW) - CONSTANT(O_NOFOLLOW) - #endif - #if defined(O_NONBLOCK) - CONSTANT(O_NONBLOCK) - #endif - #if defined(O_NDELAY) - CONSTANT(O_NDELAY) - #endif - #if defined(O_PATH) - CONSTANT(O_PATH) - #endif - #if defined(O_SYNC) - CONSTANT(O_SYNC) - #endif - #if defined(O_TMPFILE) - CONSTANT(O_TMPFILE) - #endif - #if defined(O_TRUNC) - CONSTANT(O_TRUNC) - #endif - #if defined(S_IFMT) - CONSTANT(S_IFMT) - #endif - #if defined(S_IFREG) - CONSTANT(S_IFREG) - #endif - #if defined(S_IFDIR) - CONSTANT(S_IFDIR) - #endif - #if defined(S_IFCHR) - CONSTANT(S_IFCHR) - #endif - #if defined(S_IFBLK) - CONSTANT(S_IFBLK) - #endif - #if defined(S_IFIFO) - CONSTANT(S_IFIFO) - #endif - #if defined(S_IFLNK) - CONSTANT(S_IFLNK) - #endif - #if defined(S_IFSOCK) - CONSTANT(S_IFSOCK) - #endif - #if defined(S_IRWXU) - CONSTANT(S_IRWXU) - #endif - #if defined(S_IRUSR) - CONSTANT(S_IRUSR) - #endif - #if defined(S_IWUSR) - CONSTANT(S_IWUSR) - #endif - #if defined(S_IXUSR) - CONSTANT(S_IXUSR) - #endif - #if defined(S_IRWXG) - CONSTANT(S_IRWXG) - #endif - #if defined(S_IRGRP) - CONSTANT(S_IRGRP) - #endif - #if defined(S_IWGRP) - CONSTANT(S_IWGRP) - #endif - #if defined(S_IXGRP) - CONSTANT(S_IXGRP) - #endif - #if defined(S_IRWXO) - CONSTANT(S_IRWXO) - #endif - #if defined(S_IROTH) - CONSTANT(S_IROTH) - #endif - #if defined(S_IWOTH) - CONSTANT(S_IWOTH) - #endif - #if defined(S_IXOTH) - CONSTANT(S_IXOTH) - #endif - #if defined(F_OK) - CONSTANT(F_OK) - #endif - #if defined(R_OK) - CONSTANT(R_OK) - #endif - #if defined(W_OK) - CONSTANT(W_OK) - #endif - #if defined(X_OK) - CONSTANT(X_OK) - #endif - }; - #undef CONSTANT +using ssc::runtime::url::encodeURIComponent; +using ssc::runtime::string::join; +using ssc::runtime::string::split; +namespace ssc::runtime::core::services { JSON::Object getStatsJSON (const String& source, uv_stat_t* stats) { return JSON::Object::Entries { {"source", source}, @@ -210,19 +46,23 @@ namespace SSC { }; } - void CoreFS::RequestContext::setBuffer (SharedPointer base, uint32_t len) { + void FS::RequestContext::setBuffer (SharedPointer base, uint32_t len) { this->buffer = base; this->buf.base = base.get(); this->buf.len = len; } - CoreFS::Descriptor::Descriptor (CoreFS* fs, ID id, const String& filename) - : resource(filename, { false, fs->core }), + FS::Descriptor::Descriptor (FS* fs, ID id, const String& filename) + #if SOCKET_RUNTIME_PLATFORM_ANDROID + : resource(filename, { false, fs->}), + #else + #endif + : resource(filename, { false }), fs(fs), id(id) {} - bool CoreFS::Descriptor::isDirectory () const { + bool FS::Descriptor::isDirectory () const { #if SOCKET_RUNTIME_PLATFORM_ANDROID if (this->isAndroidAssetDirectory) { return true; @@ -231,7 +71,7 @@ namespace SSC { return this->dir != nullptr; } - bool CoreFS::Descriptor::isFile () const { + bool FS::Descriptor::isFile () const { #if SOCKET_RUNTIME_PLATFORM_ANDROID if (this->androidAsset != nullptr) { return true; @@ -240,15 +80,15 @@ namespace SSC { return this->fd > 0 && this->dir == nullptr; } - bool CoreFS::Descriptor::isRetained () const { + bool FS::Descriptor::isRetained () const { return this->retained; } - bool CoreFS::Descriptor::isStale () const { + bool FS::Descriptor::isStale () const { return this->stale; } - SharedPointer CoreFS::getDescriptor (ID id) { + SharedPointer FS::getDescriptor (ID id) { Lock lock(this->mutex); if (descriptors.find(id) != descriptors.end()) { return descriptors.at(id); @@ -256,28 +96,28 @@ namespace SSC { return nullptr; } - SharedPointer CoreFS::getDescriptor (ID id) const { + SharedPointer FS::getDescriptor (ID id) const { if (descriptors.find(id) != descriptors.end()) { return descriptors.at(id); } return nullptr; } - void CoreFS::removeDescriptor (ID id) { + void FS::removeDescriptor (ID id) { Lock lock(this->mutex); if (descriptors.find(id) != descriptors.end()) { descriptors.erase(id); } } - bool CoreFS::hasDescriptor (ID id) const { + bool FS::hasDescriptor (ID id) const { return descriptors.find(id) != descriptors.end(); } - void CoreFS::retainOpenDescriptor ( + void FS::retainOpenDescriptor ( const String& seq, ID id, - const CoreModule::Callback& callback + const Callback callback ) { auto desc = getDescriptor(id); @@ -292,7 +132,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } desc->retained = true; @@ -303,17 +143,17 @@ namespace SSC { }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); } - void CoreFS::access ( + void FS::access ( const String& seq, const String& path, int mode, - const CoreModule::Callback& callback + const Callback callback ) { - this->core->dispatchEventLoop([=, this]() mutable { - auto loop = &this->core->eventLoop; + this->loop.dispatch([=, this]() mutable { + auto loop = this->loop.get(); auto desc = std::make_shared(this, 0, path); if (desc->resource.url.scheme == "socket") { @@ -325,7 +165,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } #if SOCKET_RUNTIME_PLATFORM_ANDROID else if (mode == R_OK || mode == F_OK) { @@ -362,7 +202,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } } #endif @@ -380,7 +220,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } } #endif @@ -420,7 +260,7 @@ namespace SSC { }; } - ctx->callback(ctx->seq, json, Post {}); + ctx->callback(ctx->seq, json, QueuedResponse {}); delete ctx; }); @@ -433,21 +273,21 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::chmod ( + void FS::chmod ( const String& seq, const String& path, int mode, - const CoreModule::Callback& callback + const Callback callback ) const { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { auto filename = path.c_str(); - auto loop = &this->core->eventLoop; + auto loop = this->loop.get(); auto ctx = new RequestContext(seq, callback); auto req = &ctx->req; auto err = uv_fs_chmod(loop, req, filename, mode, [](uv_fs_t* req) { @@ -471,7 +311,7 @@ namespace SSC { }; } - ctx->callback(ctx->seq, json, Post {}); + ctx->callback(ctx->seq, json, QueuedResponse {}); delete ctx; }); @@ -484,22 +324,22 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::chown ( + void FS::chown ( const String& seq, const String& path, uv_uid_t uid, uv_gid_t gid, - const CoreModule::Callback& callback + const Callback callback ) const { - core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { const auto ctx = new RequestContext(seq, callback); - const auto err = uv_fs_chown(&core->eventLoop, &ctx->req, path.c_str(), uid, gid, [](uv_fs_t* req) { + const auto err = uv_fs_chown(this->loop.get(), &ctx->req, path.c_str(), uid, gid, [](uv_fs_t* req) { auto ctx = static_cast(req->data); auto json = JSON::Object{}; @@ -520,7 +360,7 @@ namespace SSC { }; } - ctx->callback(ctx->seq, json, Post {}); + ctx->callback(ctx->seq, json, QueuedResponse {}); delete ctx; }); @@ -532,22 +372,22 @@ namespace SSC { {"message", String(uv_strerror(err))} }} }; - ctx->callback(seq, json, Post{}); + ctx->callback(seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::lchown ( + void FS::lchown ( const String& seq, const String& path, uv_uid_t uid, uv_gid_t gid, - const CoreModule::Callback& callback + const Callback callback ) const { - core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { const auto ctx = new RequestContext(seq, callback); - const auto err = uv_fs_lchown(&core->eventLoop, &ctx->req, path.c_str(), uid, gid, [](uv_fs_t* req) { + const auto err = uv_fs_lchown(this->loop.get(), &ctx->req, path.c_str(), uid, gid, [](uv_fs_t* req) { auto ctx = static_cast(req->data); auto json = JSON::Object{}; @@ -568,7 +408,7 @@ namespace SSC { }; } - ctx->callback(ctx->seq, json, Post {}); + ctx->callback(ctx->seq, json, QueuedResponse {}); delete ctx; }); @@ -580,18 +420,18 @@ namespace SSC { {"message", String(uv_strerror(err))} }} }; - ctx->callback(seq, json, Post{}); + ctx->callback(seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::close ( + void FS::close ( const String& seq, ID id, - const CoreModule::Callback& callback + const Callback callback ) { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { auto desc = getDescriptor(id); if (desc == nullptr) { @@ -605,7 +445,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } #if SOCKET_RUNTIME_PLATFORM_ANDROID @@ -622,7 +462,7 @@ namespace SSC { }; this->removeDescriptor(desc->id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } else if ( desc->resource.url.scheme == "content" || desc->resource.url.scheme == "android.resource" @@ -642,11 +482,11 @@ namespace SSC { }; this->removeDescriptor(desc->id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } #endif - auto loop = &this->core->eventLoop; + auto loop = this->loop.get(); auto ctx = new RequestContext(desc, seq, callback); auto req = &ctx->req; auto err = uv_fs_close(loop, req, desc->fd, [](uv_fs_t* req) { @@ -675,7 +515,7 @@ namespace SSC { desc->fs->removeDescriptor(desc->id); } - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; }); @@ -689,21 +529,21 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::open ( + void FS::open ( const String& seq, ID id, const String& path, int flags, int mode, - const CoreModule::Callback& callback + const Callback callback ) { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { auto desc = std::make_shared(this, id, path); #if SOCKET_RUNTIME_PLATFORM_ANDROID @@ -732,7 +572,7 @@ namespace SSC { // insert into `descriptors` map Lock lock(desc->fs->mutex); this->descriptors.insert_or_assign(desc->id, desc); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } else if ( desc->resource.url.scheme == "content" || desc->resource.url.scheme == "android.resource" @@ -753,7 +593,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } desc->fd = this->core->platform.contentResolver.getFileDescriptorFD( @@ -773,11 +613,11 @@ namespace SSC { // insert into `descriptors` map Lock lock(desc->fs->mutex); this->descriptors.insert_or_assign(desc->id, desc); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } #endif - auto loop = &this->core->eventLoop; + auto loop = this->loop.get(); auto ctx = new RequestContext(desc, seq, callback); auto req = &ctx->req; auto err = uv_fs_open(loop, req, desc->resource.path.string().c_str(), flags, mode, [](uv_fs_t* req) { @@ -838,7 +678,7 @@ namespace SSC { desc->fs->descriptors.insert_or_assign(desc->id, desc); } - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; }); @@ -852,19 +692,19 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::opendir ( + void FS::opendir ( const String& seq, ID id, const String& path, - const CoreModule::Callback& callback + const Callback callback ) { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { auto desc = std::make_shared(this, id, path); #if SOCKET_RUNTIME_PLATFORM_ANDROID @@ -916,7 +756,7 @@ namespace SSC { // insert into `descriptors` map Lock lock(desc->fs->mutex); desc->fs->descriptors.insert_or_assign(desc->id, desc); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } } else if ( desc->resource.url.scheme == "content" || @@ -942,12 +782,12 @@ namespace SSC { // insert into `descriptors` map Lock lock(desc->fs->mutex); desc->fs->descriptors.insert_or_assign(desc->id, desc); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } } #endif - auto loop = &this->core->eventLoop; + auto loop = this->loop.get(); auto ctx = new RequestContext(desc, seq, callback); auto req = &ctx->req; auto err = uv_fs_opendir(loop, req, desc->resource.path.string().c_str(), [](uv_fs_t *req) { @@ -1033,7 +873,7 @@ namespace SSC { desc->fs->descriptors.insert_or_assign(desc->id, desc); } - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; }); @@ -1047,19 +887,19 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::readdir ( + void FS::readdir ( const String& seq, ID id, size_t nentries, - const CoreModule::Callback& callback + const Callback callback ) const { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { auto desc = getDescriptor(id); if (desc == nullptr) { @@ -1073,7 +913,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } #if SOCKET_RUNTIME_PLATFORM_ANDROID @@ -1104,7 +944,7 @@ namespace SSC { {"data", entries} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } else if (desc->isAndroidContentDirectory) { Vector entries; @@ -1132,7 +972,7 @@ namespace SSC { {"data", entries} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } #endif @@ -1146,11 +986,11 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } Lock lock(desc->mutex); - auto loop = &this->core->eventLoop; + auto loop = this->loop.get(); auto ctx = new RequestContext(desc, seq, callback); auto req = &ctx->req; @@ -1189,7 +1029,7 @@ namespace SSC { }; } - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; }); @@ -1203,18 +1043,18 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::closedir ( + void FS::closedir ( const String& seq, ID id, - const CoreModule::Callback& callback + const Callback callback ) { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { auto desc = getDescriptor(id); if (desc == nullptr) { @@ -1228,7 +1068,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } #if SOCKET_RUNTIME_PLATFORM_ANDROID @@ -1244,7 +1084,7 @@ namespace SSC { }; this->removeDescriptor(desc->id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } else if (desc->isAndroidContentDirectory) { Lock lock(this->mutex); desc->isAndroidContentDirectory = false; @@ -1257,7 +1097,7 @@ namespace SSC { }; this->removeDescriptor(desc->id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } #endif @@ -1271,10 +1111,10 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } - auto loop = &this->core->eventLoop; + auto loop = this->loop.get(); auto ctx = new RequestContext(desc, seq, callback); auto req = &ctx->req; auto err = uv_fs_closedir(loop, req, desc->dir, [](uv_fs_t* req) { @@ -1303,7 +1143,7 @@ namespace SSC { desc->fs->removeDescriptor(desc->id); } - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; }); @@ -1317,16 +1157,16 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::closeOpenDescriptor ( + void FS::closeOpenDescriptor ( const String& seq, ID id, - const CoreModule::Callback& callback + const Callback callback ) { auto desc = getDescriptor(id); @@ -1341,7 +1181,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } if (desc->isDirectory()) { @@ -1351,14 +1191,14 @@ namespace SSC { } } - void CoreFS::closeOpenDescriptors (const String& seq, const CoreModule::Callback& callback) { + void FS::closeOpenDescriptors (const String& seq, const Callback callback) { return this->closeOpenDescriptors(seq, false, callback); } - void CoreFS::closeOpenDescriptors ( + void FS::closeOpenDescriptors ( const String& seq, bool preserveRetained, - const CoreModule::Callback& callback + const Callback callback ) { Lock lock(this->mutex); @@ -1402,18 +1242,18 @@ namespace SSC { } if (queued == 0) { - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); } } - void CoreFS::read ( + void FS::read ( const String& seq, ID id, size_t size, size_t offset, - const CoreModule::Callback& callback + const Callback callback ) const { - this->core->dispatchEventLoop([=, this]() mutable { + this->loop.dispatch([=, this]() mutable { auto desc = getDescriptor(id); if (desc == nullptr) { @@ -1427,7 +1267,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } #if SOCKET_RUNTIME_PLATFORM_ANDROID @@ -1440,7 +1280,7 @@ namespace SSC { {"content-length", 0} }}; - Post post {0}; + QueuedResponse post {0}; post.id = rand64(); post.body = std::make_shared(size); post.length = 0; @@ -1462,7 +1302,7 @@ namespace SSC { {"content-length", 0} }}; - Post post {0}; + QueuedResponse post {0}; post.id = rand64(); post.body = std::make_shared(size); post.length = 0; @@ -1479,7 +1319,7 @@ namespace SSC { #endif auto bytes = std::make_shared(size); - auto loop = &this->core->eventLoop; + auto loop = this->loop.get(); auto ctx = new RequestContext(desc, seq, callback); auto req = &ctx->req; @@ -1489,7 +1329,7 @@ namespace SSC { auto ctx = static_cast(req->data); auto desc = ctx->descriptor; auto json = JSON::Object {}; - Post post = {0}; + QueuedResponse post = {0}; if (uv_fs_get_result(req) < 0) { json = JSON::Object::Entries { @@ -1501,7 +1341,7 @@ namespace SSC { }} }; } else { - auto headers = Headers {{ + auto headers = http::Headers {{ {"content-type" ,"application/octet-stream"}, {"content-length", req->result} }}; @@ -1526,19 +1366,19 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::watch ( + void FS::watch ( const String& seq, ID id, const String& path, - const CoreModule::Callback& callback + const Callback callback ) { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { #if SOCKET_RUNTIME_PLATFORM_ANDROID auto json = JSON::Object::Entries { {"source", "fs.watch"}, @@ -1547,18 +1387,19 @@ namespace SSC { }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); return; #else - SharedPointer watcher; + SharedPointer watcher; { Lock lock(this->mutex); watcher = this->watchers[id]; } if (watcher == nullptr) { - watcher.reset(new FileSystemWatcher(path)); - watcher->core = this->core; + watcher.reset(new filesystem::Watcher(path)); + watcher->loop = &this->loop; + watcher->ownsLoop = false; const auto started = watcher->start([=, this]( const auto& changed, const auto& events, @@ -1566,11 +1407,11 @@ namespace SSC { ) mutable { JSON::Array::Entries eventNames; - if (std::find(events.begin(), events.end(), FileSystemWatcher::Event::RENAME) != events.end()) { + if (std::find(events.begin(), events.end(), filesystem::Watcher::Event::RENAME) != events.end()) { eventNames.push_back("rename"); } - if (std::find(events.begin(), events.end(), FileSystemWatcher::Event::CHANGE) != events.end()) { + if (std::find(events.begin(), events.end(), filesystem::Watcher::Event::CHANGE) != events.end()) { eventNames.push_back("change"); } @@ -1583,7 +1424,7 @@ namespace SSC { }} }; - callback("-1", json, Post{}); + callback("-1", json, QueuedResponse{}); }); if (!started) { @@ -1594,7 +1435,7 @@ namespace SSC { }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); return; } @@ -1611,20 +1452,20 @@ namespace SSC { }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); #endif }); } - void CoreFS::write ( + void FS::write ( const String& seq, ID id, SharedPointer bytes, size_t size, size_t offset, - const CoreModule::Callback& callback + const Callback callback ) const { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { auto desc = getDescriptor(id); if (desc == nullptr) { @@ -1638,7 +1479,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } #if SOCKET_RUNTIME_PLATFORM_ANDROID @@ -1653,11 +1494,11 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } #endif - auto loop = &this->core->eventLoop; + auto loop = this->loop.get(); auto ctx = new RequestContext(desc, seq, callback); auto req = &ctx->req; @@ -1686,7 +1527,7 @@ namespace SSC { }; } - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; }); @@ -1700,18 +1541,18 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::stat ( + void FS::stat ( const String& seq, const String& path, - const CoreModule::Callback& callback + const Callback callback ) { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { auto desc = std::make_shared(this, 0, path); #if SOCKET_RUNTIME_PLATFORM_ANDROID @@ -1724,7 +1565,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } else if ( desc->resource.url.scheme == "content" || desc->resource.url.scheme == "android.resource" @@ -1737,11 +1578,11 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } #endif - auto loop = &this->core->eventLoop; + auto loop = this->loop.get(); auto ctx = new RequestContext(desc, seq, callback); auto req = &ctx->req; auto err = uv_fs_stat(loop, req, desc->resource.path.string().c_str(), [](uv_fs_t *req) { @@ -1760,7 +1601,7 @@ namespace SSC { json = getStatsJSON("fs.stat", uv_fs_get_statbuf(req)); } - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; }); @@ -1773,18 +1614,18 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::stopWatch ( + void FS::stopWatch ( const String& seq, ID id, - const CoreModule::Callback& callback + const Callback callback ) { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { #if SOCKET_RUNTIME_PLATFORM_ANDROID auto json = JSON::Object::Entries { {"source", "fs.stopWatch"}, @@ -1793,7 +1634,7 @@ namespace SSC { }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); return; #else auto watcher = this->watchers[id]; @@ -1806,7 +1647,7 @@ namespace SSC { {"id", std::to_string(id)}, }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); } else { auto json = JSON::Object::Entries { {"source", "fs.stat"}, @@ -1816,18 +1657,18 @@ namespace SSC { }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); } #endif }); } - void CoreFS::fsync ( + void FS::fsync ( const String& seq, ID id, - const CoreModule::Callback& callback + const Callback callback ) const { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { auto desc = getDescriptor(id); if (desc == nullptr) { @@ -1841,10 +1682,10 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } - auto loop = &this->core->eventLoop; + auto loop = this->loop.get(); auto ctx = new RequestContext(desc, seq, callback); auto req = &ctx->req; auto err = uv_fs_fsync(loop, req, desc->fd, [](uv_fs_t *req) { @@ -1868,7 +1709,7 @@ namespace SSC { }; } - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; }); @@ -1882,19 +1723,19 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::ftruncate ( + void FS::ftruncate ( const String& seq, ID id, int64_t offset, - const CoreModule::Callback& callback + const Callback callback ) const { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { auto desc = getDescriptor(id); if (desc == nullptr) { @@ -1908,10 +1749,10 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } - auto loop = &this->core->eventLoop; + auto loop = this->loop.get(); auto ctx = new RequestContext(desc, seq, callback); auto req = &ctx->req; auto err = uv_fs_ftruncate(loop, req, desc->fd, offset, [](uv_fs_t *req) { @@ -1935,7 +1776,7 @@ namespace SSC { }; } - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; }); @@ -1949,18 +1790,18 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::fstat ( + void FS::fstat ( const String& seq, ID id, - const CoreModule::Callback& callback + const Callback callback ) { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { auto desc = getDescriptor(id); if (desc == nullptr) { @@ -1974,7 +1815,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } #if SOCKET_RUNTIME_PLATFORM_ANDROID @@ -1987,11 +1828,11 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } #endif - auto loop = &this->core->eventLoop; + auto loop = this->loop.get(); auto ctx = new RequestContext(desc, seq, callback); auto req = &ctx->req; auto err = uv_fs_fstat(loop, req, desc->fd, [](uv_fs_t *req) { @@ -2012,7 +1853,7 @@ namespace SSC { json = getStatsJSON("fs.fstat", uv_fs_get_statbuf(req)); } - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; }); @@ -2026,15 +1867,15 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::getOpenDescriptors ( + void FS::getOpenDescriptors ( const String& seq, - const CoreModule::Callback& callback + const Callback callback ) const { auto entries = Vector {}; @@ -2059,17 +1900,17 @@ namespace SSC { {"data", entries} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); } - void CoreFS::lstat ( + void FS::lstat ( const String& seq, const String& path, - const CoreModule::Callback& callback + const Callback callback ) { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { auto desc = std::make_shared(this, 0, path); - auto loop = &this->core->eventLoop; + auto loop = this->loop.get(); auto ctx = new RequestContext(desc, seq, callback); auto req = &ctx->req; auto err = uv_fs_lstat(loop, req, desc->resource.path.string().c_str(), [](uv_fs_t* req) { @@ -2088,7 +1929,7 @@ namespace SSC { json = getStatsJSON("fs.lstat", uv_fs_get_statbuf(req)); } - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; }); @@ -2101,21 +1942,21 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::link ( + void FS::link ( const String& seq, const String& src, const String& dest, - const CoreModule::Callback& callback + const Callback callback ) const { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { auto ctx = new RequestContext(seq, callback); - auto err = uv_fs_link(&core->eventLoop, &ctx->req, src.c_str(), dest.c_str(), [](uv_fs_t* req) { + auto err = uv_fs_link(this->loop.get(), &ctx->req, src.c_str(), dest.c_str(), [](uv_fs_t* req) { auto ctx = static_cast(req->data); auto json = JSON::Object{}; @@ -2136,7 +1977,7 @@ namespace SSC { }; } - ctx->callback(ctx->seq, json, Post {}); + ctx->callback(ctx->seq, json, QueuedResponse {}); delete ctx; }); @@ -2148,22 +1989,22 @@ namespace SSC { {"message", String(uv_strerror(err))} }} }; - ctx->callback(seq, json, Post{}); + ctx->callback(seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::symlink ( + void FS::symlink ( const String& seq, const String& src, const String& dest, int flags, - const CoreModule::Callback& callback + const Callback callback ) const { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { auto ctx = new RequestContext(seq, callback); - auto err = uv_fs_symlink(&core->eventLoop, &ctx->req, src.c_str(), dest.c_str(), flags, [](uv_fs_t* req) { + auto err = uv_fs_symlink(this->loop.get(), &ctx->req, src.c_str(), dest.c_str(), flags, [](uv_fs_t* req) { auto ctx = static_cast(req->data); auto json = JSON::Object{}; @@ -2184,7 +2025,7 @@ namespace SSC { }; } - ctx->callback(ctx->seq, json, Post {}); + ctx->callback(ctx->seq, json, QueuedResponse {}); delete ctx; }); @@ -2196,20 +2037,20 @@ namespace SSC { {"message", String(uv_strerror(err))} }} }; - ctx->callback(seq, json, Post{}); + ctx->callback(seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::unlink ( + void FS::unlink ( const String& seq, const String& path, - const CoreModule::Callback& callback + const Callback callback ) const { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { auto filename = path.c_str(); - auto loop = &this->core->eventLoop; + auto loop = this->loop.get(); auto ctx = new RequestContext(seq, callback); auto req = &ctx->req; auto err = uv_fs_unlink(loop, req, filename, [](uv_fs_t* req) { @@ -2233,7 +2074,7 @@ namespace SSC { }; } - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; }); @@ -2246,20 +2087,20 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::readlink ( + void FS::readlink ( const String& seq, const String& path, - const CoreModule::Callback& callback + const Callback callback ) const { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { auto ctx = new RequestContext(seq, callback); - auto err = uv_fs_readlink(&core->eventLoop, &ctx->req, path.c_str(), [](uv_fs_t* req) { + auto err = uv_fs_readlink(this->loop.get(), &ctx->req, path.c_str(), [](uv_fs_t* req) { auto ctx = static_cast(req->data); auto json = JSON::Object{}; @@ -2280,7 +2121,7 @@ namespace SSC { }; } - ctx->callback(ctx->seq, json, Post {}); + ctx->callback(ctx->seq, json, QueuedResponse {}); delete ctx; }); @@ -2292,21 +2133,21 @@ namespace SSC { {"message", String(uv_strerror(err))} }} }; - ctx->callback(seq, json, Post{}); + ctx->callback(seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::realpath ( + void FS::realpath ( const String& seq, const String& path, - const CoreModule::Callback& callback + const Callback callback ) { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { auto filename = path.c_str(); auto desc = std::make_shared(this, 0, filename); auto ctx = new RequestContext(desc, seq, callback); - auto err = uv_fs_realpath(&core->eventLoop, &ctx->req, path.c_str(), [](uv_fs_t* req) { + auto err = uv_fs_realpath(this->loop.get(), &ctx->req, path.c_str(), [](uv_fs_t* req) { auto ctx = static_cast(req->data); auto json = JSON::Object{}; @@ -2346,7 +2187,7 @@ namespace SSC { }; } - ctx->callback(ctx->seq, json, Post {}); + ctx->callback(ctx->seq, json, QueuedResponse {}); delete ctx; }); @@ -2358,20 +2199,20 @@ namespace SSC { {"message", String(uv_strerror(err))} }} }; - ctx->callback(seq, json, Post{}); + ctx->callback(seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::rename ( + void FS::rename ( const String& seq, const String& pathA, const String& pathB, - const CoreModule::Callback& callback + const Callback callback ) const { - this->core->dispatchEventLoop([=, this]() { - auto loop = &this->core->eventLoop; + this->loop.dispatch([=, this]() { + auto loop = this->loop.get(); auto ctx = new RequestContext(seq, callback); auto req = &ctx->req; auto src = pathA.c_str(); @@ -2397,7 +2238,7 @@ namespace SSC { }; } - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; }); @@ -2410,20 +2251,20 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::copyFile ( + void FS::copyFile ( const String& seq, const String& pathA, const String& pathB, int flags, - const CoreModule::Callback& callback + const Callback callback ) { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { auto src = std::make_shared(this, 0, pathA); auto dst = std::make_shared(this, 0, pathB); @@ -2442,11 +2283,11 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } #endif - auto loop = &this->core->eventLoop; + auto loop = this->loop.get(); auto ctx = new RequestContext(seq, callback); auto req = &ctx->req; auto err = uv_fs_copyfile(loop, req, src->resource.path.string().c_str(), dst->resource.path.string().c_str(), flags, [](uv_fs_t* req) { @@ -2470,7 +2311,7 @@ namespace SSC { }; } - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; }); @@ -2483,20 +2324,20 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::rmdir ( + void FS::rmdir ( const String& seq, const String& path, - const CoreModule::Callback& callback + const Callback callback ) const { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { auto filename = path.c_str(); - auto loop = &this->core->eventLoop; + auto loop = this->loop.get(); auto ctx = new RequestContext(seq, callback); auto req = &ctx->req; auto err = uv_fs_rmdir(loop, req, filename, [](uv_fs_t* req) { @@ -2520,7 +2361,7 @@ namespace SSC { }; } - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; }); @@ -2533,23 +2374,23 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::mkdir ( + void FS::mkdir ( const String& seq, const String& path, int mode, bool recursive, - const CoreModule::Callback& callback + const Callback callback ) const { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { int err = 0; auto filename = path.c_str(); - auto loop = &this->core->eventLoop; + auto loop = this->loop.get(); auto ctx = new RequestContext(seq, callback); ctx->recursive = recursive; auto req = &ctx->req; @@ -2577,7 +2418,7 @@ namespace SSC { }; } - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; }; @@ -2619,22 +2460,22 @@ namespace SSC { }} }; - ctx->callback(ctx->seq, json, Post{}); + ctx->callback(ctx->seq, json, QueuedResponse{}); delete ctx; } }); } - void CoreFS::constants ( + void FS::constants ( const String& seq, - const CoreModule::Callback& callback + const Callback callback ) const { - static const auto data = JSON::Object(FS_CONSTANTS); + static const auto data = JSON::Object(filesystem::constants()); static const auto json = JSON::Object::Entries { {"source", "fs.constants"}, {"data", data} }; - callback(seq, json, Post {}); + callback(seq, json, QueuedResponse {}); } } diff --git a/src/runtime/core/services/fs.hh b/src/runtime/core/services/fs.hh new file mode 100644 index 0000000000..047db18ccd --- /dev/null +++ b/src/runtime/core/services/fs.hh @@ -0,0 +1,144 @@ +#ifndef SOCKET_RUNTIME_CORE_SERVICES_FS_H +#define SOCKET_RUNTIME_CORE_SERVICES_FS_H + +#include "../../filesystem.hh" +#include "../../debug.hh" +#include "../../core.hh" +#include "../../ipc.hh" + +namespace ssc::runtime::core::services { + class FS : public core::Service { + public: + using ID = uint64_t; + + struct Descriptor { + ID id; + Atomic retained = false; + Atomic stale = false; + filesystem::Resource resource; + Mutex mutex; + uv_dir_t *dir = nullptr; + uv_file fd = 0; + FS* fs = nullptr; + + #if SOCKET_RUNTIME_PLATFORM_ANDROID + // asset state + Android::Asset* androidAsset = nullptr; + Queue androidAssetDirectoryEntries; + Queue androidContentDirectoryEntries; + Android::ContentResolver::FileDescriptor androidContent = nullptr; + // type predicates + bool isAndroidAssetDirectory = false; + bool isAndroidContentDirectory = false; + bool isAndroidContent = false; + // descriptor offsets + off_t androidAssetOffset = 0; + off_t androidAssetLength = 0; + off_t androidContentOffset = 0; + off_t androidContentLength = 0; + #endif + + Descriptor (FS* fs, ID, const String& filename); + bool isDirectory () const; + bool isFile () const; + bool isRetained () const; + bool isStale () const; + }; + + struct RequestContext : core::Service::RequestContext { + ID id; + SharedPointer descriptor = nullptr; + SharedPointer buffer = nullptr; + debug::Tracer tracer; + uv_fs_t req; + uv_buf_t buf; + // 256 which corresponds to DirectoryHandle.MAX_BUFFER_SIZE + uv_dirent_t dirents[256]; + int offset = 0; + int result = 0; + bool recursive; + + RequestContext () = delete; + RequestContext (SharedPointer descriptor) + : RequestContext(descriptor, "", nullptr) + {} + + RequestContext (const ipc::Message::Seq& seq, const Callback callback) + : RequestContext(nullptr, seq, callback) + {} + + RequestContext ( + SharedPointer descriptor, + const ipc::Message::Seq&, + const Callback + ) : tracer("FS::RequestContext") + { + this->id = rand64(); + this->seq = seq; + this->req.data = (void*) this; + this->callback = callback; + this->descriptor = descriptor; + this->recursive = false; + this->req.loop = nullptr; + } + + ~RequestContext () { + if (this->req.loop) { + uv_fs_req_cleanup(&this->req); + } + } + + void setBuffer (SharedPointer base, uint32_t size); + }; + + Map> watchers; + Map> descriptors; + Mutex mutex; + + FS (const Options& options) + : core::Service(options) + {} + + SharedPointer getDescriptor (ID) const; + SharedPointer getDescriptor (ID); + void removeDescriptor (ID); + bool hasDescriptor (ID) const; + + void constants (const ipc::Message::Seq&, const Callback) const; + void access (const ipc::Message::Seq&, const String&, int, const Callback); + void chmod (const ipc::Message::Seq&, const String&, int, const Callback) const; + void chown (const ipc::Message::Seq&, const String&, uv_uid_t, uv_gid_t, const Callback) const; + void lchown (const ipc::Message::Seq&, const String&, uv_uid_t, uv_gid_t, const Callback) const; + + void close (const ipc::Message::Seq&, ID, const Callback); + void copyFile (const ipc::Message::Seq&, const String&, const String&, int, const Callback); + void closedir (const ipc::Message::Seq&, ID, const Callback); + + void closeOpenDescriptor (const ipc::Message::Seq&, ID, const Callback); + void closeOpenDescriptors (const ipc::Message::Seq&, const Callback); + void closeOpenDescriptors (const ipc::Message::Seq&, bool, const Callback); + void fstat (const ipc::Message::Seq&, ID, const Callback); + void fsync (const ipc::Message::Seq&, ID, const Callback) const; + void ftruncate (const ipc::Message::Seq&, ID, int64_t, const Callback) const; + void getOpenDescriptors (const ipc::Message::Seq&, const Callback) const; + void lstat (const ipc::Message::Seq&, const String&, const Callback); + void link (const ipc::Message::Seq&, const String&, const String&, const Callback) const; + void symlink (const ipc::Message::Seq&, const String&, const String&, int , const Callback) const; + void mkdir (const ipc::Message::Seq&, const String&, int, bool, const Callback) const; + void readlink (const ipc::Message::Seq&, const String&, const Callback) const; + void realpath (const ipc::Message::Seq&, const String&, const Callback); + void open (const ipc::Message::Seq&, ID, const String&, int, int, const Callback); + void opendir (const ipc::Message::Seq&, ID, const String&, const Callback); + void read (const ipc::Message::Seq&, ID, size_t, size_t, const Callback) const; + void readdir (const ipc::Message::Seq&, ID, size_t, const Callback) const; + void retainOpenDescriptor (const ipc::Message::Seq&, ID, const Callback); + void rename (const ipc::Message::Seq&, const String&, const String&, const Callback) const; + void rmdir (const ipc::Message::Seq&, const String&, const Callback) const; + void stat (const ipc::Message::Seq&, const String&, const Callback); + void stopWatch (const ipc::Message::Seq&, ID, const Callback); + void unlink (const ipc::Message::Seq&, const String&, const Callback) const; + void watch (const ipc::Message::Seq&, ID, const String&, const Callback); + void write (const ipc::Message::Seq&, ID, SharedPointer, size_t, size_t, const Callback) const; + }; +} +#endif diff --git a/src/core/modules/geolocation.cc b/src/runtime/core/services/geolocation.cc similarity index 89% rename from src/core/modules/geolocation.cc rename to src/runtime/core/services/geolocation.cc index 49c5a95261..5ab39e3154 100644 --- a/src/core/modules/geolocation.cc +++ b/src/runtime/core/services/geolocation.cc @@ -1,7 +1,10 @@ +#include "../../debug.hh" +#include "../../config.hh" + #include "geolocation.hh" -#include "../debug.hh" #if SOCKET_RUNTIME_PLATFORM_APPLE +using ssc::runtime::config::getUserConfig; @implementation SSCLocationPositionWatcher + (SSCLocationPositionWatcher*) positionWatcherWithIdentifier: (NSInteger) identifier completion: (void (^)(CLLocation*)) completion @@ -85,7 +88,7 @@ - (BOOL) getCurrentPositionWithCompletion: (void (^)(NSError*, CLLocation*)) completion { return [self attemptActivationWithCompletion: ^(BOOL isAuthorized) { - auto userConfig = SSC::getUserConfig(); + auto userConfig = getUserConfig(); if (!isAuthorized) { auto reason = @("Location observer could not be activated"); @@ -139,7 +142,7 @@ } const auto performedActivation = [self attemptActivationWithCompletion: ^(BOOL isAuthorized) { - auto userConfig = SSC::getUserConfig(); + auto userConfig = getUserConfig(); if (!isAuthorized) { auto error = [NSError errorWithDomain: @(userConfig["meta_bundle_identifier"].c_str()) @@ -250,7 +253,7 @@ } - (void) locationManagerDidChangeAuthorization: (CLLocationManager*) locationManager { - using namespace SSC; + using namespace ssc::runtime; auto activationCompletions = [NSArray arrayWithArray: self.locationObserver.activationCompletions]; if ( #if !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR @@ -298,9 +301,9 @@ #endif -namespace SSC { - CoreGeolocation::CoreGeolocation (Core* core) - : CoreModule(core), +namespace ssc::runtime::core::services { + Geolocation::Geolocation (const Options& options) + : core::Service(options), permissionChangeObservers() { #if SOCKET_RUNTIME_PLATFORM_APPLE @@ -311,7 +314,7 @@ namespace SSC { #endif } - CoreGeolocation::~CoreGeolocation () { + Geolocation::~Geolocation () { #if SOCKET_RUNTIME_PLATFORM_APPLE #if !__has_feature(objc_arc) [this->locationObserver release]; @@ -324,9 +327,9 @@ namespace SSC { #endif } - void CoreGeolocation::getCurrentPosition ( + void Geolocation::getCurrentPosition ( const String& seq, - const CoreModule::Callback& callback + const Callback callback ) const { bool performedActivation = false; #if SOCKET_RUNTIME_PLATFORM_APPLE @@ -340,12 +343,12 @@ namespace SSC { const auto json = JSON::Object::Entries { {"err", JSON::Object::Entries { - {"type", "CoreGeolocationPositionError"}, + {"type", "GeolocationPositionError"}, {"message", message} }} }; - return callback(seq, json, Post {}); + return callback(seq, json, QueuedResponse {}); } const auto heading = this->locationObserver.locationManager.heading; @@ -362,26 +365,26 @@ namespace SSC { }} }; - callback(seq, json, Post {}); + callback(seq, json, QueuedResponse {}); }]; #endif if (!performedActivation) { const auto json = JSON::Object::Entries { {"err", JSON::Object::Entries { - {"type", "CoreGeolocationPositionError"}, + {"type", "GeolocationPositionError"}, {"message", "Failed to get position"} }} }; - callback(seq, json, Post {}); + callback(seq, json, QueuedResponse {}); } } - void CoreGeolocation::watchPosition ( + void Geolocation::watchPosition ( const String& seq, WatchID id, - const CoreModule::Callback& callback + const Callback callback ) { #if SOCKET_RUNTIME_PLATFORM_APPLE const int identifier = [this->locationObserver watchPositionForIdentifier: id completion: ^(NSError* error, CLLocation* location) { @@ -394,12 +397,12 @@ namespace SSC { const auto json = JSON::Object::Entries { {"err", JSON::Object::Entries { - {"type", "CoreGeolocationPositionError"}, + {"type", "GeolocationPositionError"}, {"message", message} }} }; - return callback(seq, json, Post {}); + return callback(seq, json, QueuedResponse {}); } const auto heading = this->locationObserver.locationManager.heading; @@ -419,7 +422,7 @@ namespace SSC { }} }; - callback("-1", json, Post {}); + callback("-1", json, QueuedResponse {}); }]; if (identifier != -1) { @@ -429,49 +432,40 @@ namespace SSC { }} }; - return callback(seq, json, Post {}); + return callback(seq, json, QueuedResponse {}); } #endif const auto json = JSON::Object::Entries { {"err", JSON::Object::Entries { - {"type", "CoreGeolocationPositionError"}, + {"type", "GeolocationPositionError"}, {"message", "Failed to watch position"} }} }; - callback(seq, json, Post {}); + callback(seq, json, QueuedResponse {}); } - void CoreGeolocation::clearWatch ( + void Geolocation::clearWatch ( const String& seq, WatchID id, - const CoreModule::Callback& callback + const Callback callback ) { #if SOCKET_RUNTIME_PLATFORM_APPLE [this->locationObserver clearWatch: id]; #endif - callback(seq, JSON::Object {}, Post {}); + callback(seq, JSON::Object {}, QueuedResponse {}); } - template<> bool CoreModule::template Observers>::add( - const CoreModule::Observer&, - CoreModule::Observer::Callback - ); - - template<> bool CoreGeolocation::PermissionChangeObservers::remove( - const CoreGeolocation::PermissionChangeObserver& - ); - - bool CoreGeolocation::addPermissionChangeObserver ( + bool Geolocation::addPermissionChangeObserver ( const PermissionChangeObserver& observer, const PermissionChangeObserver::Callback callback ) { return this->permissionChangeObservers.add(observer, callback); } - bool CoreGeolocation::removePermissionChangeObserver (const PermissionChangeObserver& observer) { + bool Geolocation::removePermissionChangeObserver (const PermissionChangeObserver& observer) { return this->permissionChangeObservers.remove(observer); } } diff --git a/src/core/modules/geolocation.hh b/src/runtime/core/services/geolocation.hh similarity index 82% rename from src/core/modules/geolocation.hh rename to src/runtime/core/services/geolocation.hh index c3ee30a4c3..cd46b30a83 100644 --- a/src/core/modules/geolocation.hh +++ b/src/runtime/core/services/geolocation.hh @@ -1,12 +1,10 @@ -#ifndef SOCKET_RUNTIME_CORE_MODULE_GEOLOCATION_H -#define SOCKET_RUNTIME_CORE_MODULE_GEOLOCATION_H +#ifndef SOCKET_RUNTIME_CORE_SERVICES_GEOLOCATION_H +#define SOCKET_RUNTIME_CORE_SERVICES_GEOLOCATION_H -#include "../module.hh" +#include "../../core.hh" -namespace SSC { - // forward - class Core; - class CoreGeolocation; +namespace ssc::runtime::core::services { + class Geolocation; } #if SOCKET_RUNTIME_PLATFORM_APPLE @@ -49,7 +47,7 @@ namespace SSC { @property (atomic, retain) NSMutableArray* activationCompletions; @property (atomic, retain) NSMutableArray* locationRequestCompletions; @property (atomic, retain) NSMutableArray* locationWatchers; -@property (nonatomic) SSC::CoreGeolocation* geolocation; +W@property (nonatomic) ssc::runtime::core::services::Geolocation* geolocation; @property (atomic, assign) BOOL isAuthorized; - (BOOL) attemptActivation; - (BOOL) attemptActivationWithCompletion: (void (^)(BOOL)) completion; @@ -60,12 +58,12 @@ namespace SSC { @end #endif -namespace SSC { - class CoreGeolocation : public CoreModule { +namespace ssc::runtime::core::services { + class Geolocation : public core::Service { public: using WatchID = uint64_t; - using PermissionChangeObserver = CoreModule::Observer; - using PermissionChangeObservers = CoreModule::Observers; + using PermissionChangeObserver = Observer; + using PermissionChangeObservers = Observers; #if SOCKET_RUNTIME_PLATFORM_APPLE SSCLocationObserver* locationObserver = nullptr; @@ -75,24 +73,24 @@ namespace SSC { PermissionChangeObservers permissionChangeObservers; - CoreGeolocation (Core* core); - ~CoreGeolocation (); + Geolocation (const Options&); + ~Geolocation (); void getCurrentPosition ( const String& seq, - const CoreModule::Callback& callback + const Callback callback ) const; void watchPosition ( const String& seq, const WatchID id, - const CoreModule::Callback& callback + const Callback callback ); void clearWatch ( const String& seq, const WatchID id, - const CoreModule::Callback& callback + const Callback callback ); bool removePermissionChangeObserver ( diff --git a/src/runtime/core/services/media_devices.cc b/src/runtime/core/services/media_devices.cc new file mode 100644 index 0000000000..0d745bfc26 --- /dev/null +++ b/src/runtime/core/services/media_devices.cc @@ -0,0 +1,21 @@ +#include "media_devices.hh" + +namespace ssc::runtime::core::services { + MediaDevices::MediaDevices (const Options& options) + : core::Service(options), + permissionChangeObservers() + {} + + MediaDevices::~MediaDevices () {} + + bool MediaDevices::addPermissionChangeObserver ( + const PermissionChangeObserver& observer, + const PermissionChangeObserver::Callback callback + ) { + return this->permissionChangeObservers.add(observer, callback); + } + + bool MediaDevices::removePermissionChangeObserver (const PermissionChangeObserver& observer) { + return this->permissionChangeObservers.remove(observer); + } +} diff --git a/src/runtime/core/services/media_devices.hh b/src/runtime/core/services/media_devices.hh new file mode 100644 index 0000000000..6adc713f27 --- /dev/null +++ b/src/runtime/core/services/media_devices.hh @@ -0,0 +1,27 @@ +#ifndef SOCKET_RUNTIME_CORE_SERVICES_MEDIA_DEVICES_H +#define SOCKET_RUNTIME_CORE_SERVICES_MEDIA_DEVICES_H + +#include "../../core.hh" + +namespace ssc::runtime::core::services { + class MediaDevices : public core::Service { + public: + using PermissionChangeObserver = Observer; + using PermissionChangeObservers = Observers; + + PermissionChangeObservers permissionChangeObservers; + + MediaDevices (const Options& options); + ~MediaDevices (); + + bool removePermissionChangeObserver ( + const PermissionChangeObserver& observer + ); + + bool addPermissionChangeObserver ( + const PermissionChangeObserver& observer, + const PermissionChangeObserver::Callback callback + ); + }; +} +#endif diff --git a/src/core/modules/network_status.cc b/src/runtime/core/services/network_status.cc similarity index 86% rename from src/core/modules/network_status.cc rename to src/runtime/core/services/network_status.cc index 7cd15379c7..20e5499fce 100644 --- a/src/core/modules/network_status.cc +++ b/src/runtime/core/services/network_status.cc @@ -1,8 +1,8 @@ #include "network_status.hh" -namespace SSC { - CoreNetworkStatus::CoreNetworkStatus (Core* core) - : CoreModule(core) +namespace ssc::runtime::core::services { + NetworkStatus::NetworkStatus (const Options& options) + : core::Service(options) { #if SOCKET_RUNTIME_PLATFORM_APPLE dispatch_queue_attr_t attrs = dispatch_queue_attr_make_with_qos_class( @@ -21,7 +21,7 @@ namespace SSC { #endif } - CoreNetworkStatus::~CoreNetworkStatus () { + NetworkStatus::~NetworkStatus () { #if SOCKET_RUNTIME_PLATFORM_APPLE this->stop(); dispatch_release(this->queue); @@ -30,7 +30,7 @@ namespace SSC { #endif } - bool CoreNetworkStatus::start () { + bool NetworkStatus::start () { this->stop(); #if SOCKET_RUNTIME_PLATFORM_APPLE this->monitor = nw_path_monitor_create(); @@ -83,7 +83,7 @@ namespace SSC { this->monitor, "network-changed", G_CALLBACK((+[](GNetworkMonitor* monitor, gboolean networkAvailable, gpointer userData) { - auto coreNetworkStatus = reinterpret_cast(userData); + auto coreNetworkStatus = reinterpret_cast(userData); if (coreNetworkStatus) { const auto json = JSON::Object::Entries { {"name", networkAvailable ? "online" : "offline"}, @@ -100,7 +100,7 @@ namespace SSC { return false; } - bool CoreNetworkStatus::stop () { + bool NetworkStatus::stop () { #if SOCKET_RUNTIME_PLATFORM_APPLE if (this->monitor) { nw_path_monitor_cancel(this->monitor); @@ -118,11 +118,11 @@ namespace SSC { return false; } - bool CoreNetworkStatus::addObserver (const Observer& observer, const Observer::Callback callback) { + bool NetworkStatus::addObserver (const Observer& observer, const Observer::Callback callback) { return this->observers.add(observer, callback); } - bool CoreNetworkStatus::removeObserver (const Observer& observer) { + bool NetworkStatus::removeObserver (const Observer& observer) { return this->observers.remove(observer); } } diff --git a/src/core/modules/network_status.hh b/src/runtime/core/services/network_status.hh similarity index 52% rename from src/core/modules/network_status.hh rename to src/runtime/core/services/network_status.hh index 5d59c7e9f9..1fcb72909b 100644 --- a/src/core/modules/network_status.hh +++ b/src/runtime/core/services/network_status.hh @@ -1,14 +1,13 @@ -#ifndef SOCKET_RUNTIME_CORE_MODULE_NETWORK_STATUS_H -#define SOCKET_RUNTIME_CORE_MODULE_NETWORK_STATUS_H +#ifndef SOCKET_RUNTIME_CORE_SERVICES_NETWORK_STATUS_H +#define SOCKET_RUNTIME_CORE_SERVICES_NETWORK_STATUS_H -#include "../json.hh" -#include "../module.hh" +#include "../../core.hh" -namespace SSC { - class CoreNetworkStatus : public CoreModule { +namespace ssc::runtime::core::services { + class NetworkStatus : public core::Service { public: - using Observer = CoreModule::Observer; - using Observers = CoreModule::Observers; + using Observer = Observer; + using Observers = Observers; #if SOCKET_RUNTIME_PLATFORM_APPLE dispatch_queue_t queue = nullptr; @@ -20,11 +19,11 @@ namespace SSC { Observers observers; - CoreNetworkStatus (Core*); - ~CoreNetworkStatus (); + NetworkStatus (const Options&); + ~NetworkStatus (); - bool start (); - bool stop (); + bool start () override; + bool stop () override; bool addObserver ( const Observer& observer, const Observer::Callback callback = nullptr diff --git a/src/core/modules/notifications.cc b/src/runtime/core/services/notifications.cc similarity index 86% rename from src/core/modules/notifications.cc rename to src/runtime/core/services/notifications.cc index 1e8668e9df..4f6036dcc1 100644 --- a/src/core/modules/notifications.cc +++ b/src/runtime/core/services/notifications.cc @@ -1,7 +1,7 @@ -#include "../resource.hh" -#include "../debug.hh" -#include "../core.hh" -#include "../url.hh" +#include "../../filesystem.hh" +#include "../../debug.hh" +#include "../../core.hh" +#include "../../url.hh" #include "notifications.hh" @@ -11,7 +11,7 @@ didReceiveNotificationResponse: (UNNotificationResponse*) response withCompletionHandler: (void (^)(void)) completionHandler { - using namespace SSC; + using namespace ssc::runtime; const auto id = String(response.notification.request.identifier.UTF8String); const auto action = ( @@ -34,7 +34,7 @@ willPresentNotification: (UNNotification*) notification withCompletionHandler: (void (^)(UNNotificationPresentationOptions options)) completionHandler { - using namespace SSC; + using namespace ssc::runtime; UNNotificationPresentationOptions options = UNNotificationPresentationOptionList; const auto __block id = String(notification.request.identifier.UTF8String); @@ -81,23 +81,24 @@ @end #endif -namespace SSC { - const JSON::Object CoreNotifications::Notification::json () const { +namespace ssc::runtime::core::services { + const JSON::Object Notifications::Notification::json () const { return JSON::Object::Entries { {"id", this->identifier} }; } - CoreNotifications::CoreNotifications (Core* core) - : CoreModule(core), + Notifications::Notifications (const Options& options) + : core::Service(options), + permissions(options), permissionChangeObservers(), notificationResponseObservers(), notificationPresentedObservers() {} - CoreNotifications::~CoreNotifications () { + Notifications::~Notifications () { #if SOCKET_RUNTIME_PLATFORM_APPLE - if (!core->options.features.useNotifications) return; + if (!this->enabled) return; auto notificationCenter = [UNUserNotificationCenter currentNotificationCenter]; @@ -116,8 +117,11 @@ namespace SSC { #endif } - void CoreNotifications::start () { - if (!this->core->options.features.useNotifications) return; + bool Notifications::start () { + if (!this->enabled) { + return false; + } + this->stop(); #if SOCKET_RUNTIME_PLATFORM_APPLE auto notificationCenter = [UNUserNotificationCenter currentNotificationCenter]; @@ -157,58 +161,65 @@ namespace SSC { ]; }]; #endif + + return true; } - void CoreNotifications::stop () { - if (!this->core->options.features.useNotifications) return; + bool Notifications::stop () { + if (!this->enabled) { + return false; + } + #if SOCKET_RUNTIME_PLATFORM_APPLE if (this->userNotificationCenterPollTimer) { [this->userNotificationCenterPollTimer invalidate]; this->userNotificationCenterPollTimer = nullptr; } #endif + + return true; } - bool CoreNotifications::addPermissionChangeObserver ( + bool Notifications::addPermissionChangeObserver ( const PermissionChangeObserver& observer, - const PermissionChangeObserver::Callback& callback + const PermissionChangeObserver::Callback callback ) { return this->permissionChangeObservers.add(observer, callback); } - bool CoreNotifications::removePermissionChangeObserver (const PermissionChangeObserver& observer) { + bool Notifications::removePermissionChangeObserver (const PermissionChangeObserver& observer) { return this->permissionChangeObservers.remove(observer); } - bool CoreNotifications::addNotificationResponseObserver ( + bool Notifications::addNotificationResponseObserver ( const NotificationResponseObserver& observer, - const NotificationResponseObserver::Callback& callback + const NotificationResponseObserver::Callback callback ) { return this->notificationResponseObservers.add(observer, callback); } - bool CoreNotifications::removeNotificationResponseObserver (const NotificationResponseObserver& observer) { + bool Notifications::removeNotificationResponseObserver (const NotificationResponseObserver& observer) { return this->notificationResponseObservers.remove(observer); } - bool CoreNotifications::addNotificationPresentedObserver ( + bool Notifications::addNotificationPresentedObserver ( const NotificationPresentedObserver& observer, - const NotificationPresentedObserver::Callback& callback + const NotificationPresentedObserver::Callback callback ) { return this->notificationPresentedObservers.add(observer, callback); } - bool CoreNotifications::removeNotificationPresentedObserver (const NotificationPresentedObserver& observer) { + bool Notifications::removeNotificationPresentedObserver (const NotificationPresentedObserver& observer) { return this->notificationPresentedObservers.remove(observer); } - bool CoreNotifications::show (const ShowOptions& options, const ShowCallback& callback) { + bool Notifications::show (const ShowOptions& options, const ShowCallback callback) { if (options.id.size() == 0) { - callback(ShowResult { "Missing 'id' in CoreNotifications::ShowOptions" }); + callback(ShowResult { "Missing 'id' in Notifications::ShowOptions" }); return false; } - if (!this->core->permissions.hasRuntimePermission("notifications")) { + if (!this->permissions.hasRuntimePermission("notifications")) { callback(ShowResult { "Runtime permission is disabled for 'notifications'" }); return false; } @@ -242,7 +253,7 @@ namespace SSC { const auto url = URL(options.icon); if (options.icon.starts_with("socket://")) { - const auto path = FileResource::getResourcePath(url.pathname); + const auto path = filesystem::Resource::getResourcePath(url.pathname); iconURL = [NSURL fileURLWithPath: @(path.string().c_str())]; } else { iconURL = [NSURL fileURLWithPath: @(url.href.c_str())]; @@ -284,9 +295,9 @@ namespace SSC { [attachments addObject: attachment]; } else { // using an asset from the resources directory will require a code signed application - const auto path = FileResource::getResourcePath(String("icon.png")); + const auto path = filesystem::Resource::getResourcePath(String("icon.png")); - if (FileResource(path).exists()) { + if (filesystem::Resource(path).exists()) { const auto iconURL = [NSURL fileURLWithPath: @(path.string().c_str())]; const auto types = [UTType typesWithTag: iconURL.pathExtension @@ -327,7 +338,7 @@ namespace SSC { const auto url = URL(options.image); if (options.image.starts_with("socket://")) { - const auto path = FileResource::getResourcePath(url.pathname); + const auto path = filesystem::Resource::getResourcePath(url.pathname); imageURL = [NSURL fileURLWithPath: @(path.string().c_str())]; } else { imageURL = [NSURL fileURLWithPath: @(url.href.c_str())]; @@ -389,7 +400,7 @@ namespace SSC { : "An unknown error occurred" ); - this->core->dispatchEventLoop([=] () { + this->loop.dispatch([=] () { cb(ShowResult { message }); }); #if !__has_feature(objc_arc) @@ -398,14 +409,14 @@ namespace SSC { return; } - this->core->dispatchEventLoop([=] () { + this->loop.dispatch([=] () { cb(ShowResult { "", id }); }); }]; return true; #elif SOCKET_RUNTIME_PLATFORM_ANDROID - const auto attachment = Android::JNIEnvironmentAttachment(this->core->platform.jvm); + const auto attachment = Android::JNIEnvironmentAttachment(this->dispatcher.jvm); // `activity.showNotification( // id, // title, @@ -421,7 +432,7 @@ namespace SSC { const auto success = CallClassMethodFromAndroidEnvironment( attachment.env, Boolean, - this->core->platform.activity, + this->dispatcher.activity, "showNotification", "(" "Ljava/lang/String;" // id @@ -447,7 +458,7 @@ namespace SSC { attachment.env->NewStringUTF(options.vibrate.c_str()) ); - this->core->dispatchEventLoop([=, this] () { + this->loop.dispatch([=, this] () { callback(ShowResult { "", options.id }); this->notificationPresentedObservers.dispatch(JSON::Object::Entries { {"id", options.id} @@ -458,8 +469,8 @@ namespace SSC { return false; } - bool CoreNotifications::close (const Notification& notification) { - if (!this->core->permissions.hasRuntimePermission("notifications")) { + bool Notifications::close (const Notification& notification) { + if (!this->permissions.hasRuntimePermission("notifications")) { return false; } #if SOCKET_RUNTIME_PLATFORM_APPLE @@ -476,7 +487,7 @@ namespace SSC { this->notificationResponseObservers.dispatch(json); return true; #elif SOCKET_RUNTIME_PLATFORM_ANDROID - const auto attachment = Android::JNIEnvironmentAttachment(this->core->platform.jvm); + const auto attachment = Android::JNIEnvironmentAttachment(this->dispatcher.jvm); // `activity.showNotification( // id, // tag, @@ -484,7 +495,7 @@ namespace SSC { const auto success = CallClassMethodFromAndroidEnvironment( attachment.env, Boolean, - this->core->platform.activity, + this->dispatcher.activity, "closeNotification", "(" "Ljava/lang/String;" // id @@ -494,7 +505,7 @@ namespace SSC { attachment.env->NewStringUTF(notification.tag.c_str()) ); - this->core->dispatchEventLoop([=, this] () { + this->loop.dispatch([=, this] () { const auto json = JSON::Object::Entries { {"id", notification.identifier}, {"action", "dismiss"} @@ -508,7 +519,7 @@ namespace SSC { return false; } - void CoreNotifications::list (const ListCallback& callback) const { + void Notifications::list (const ListCallback callback) const { #if SOCKET_RUNTIME_PLATFORM_APPLE auto notificationCenter = [UNUserNotificationCenter currentNotificationCenter]; [notificationCenter getDeliveredNotificationsWithCompletionHandler: ^(NSArray *notifications) { diff --git a/src/core/modules/notifications.hh b/src/runtime/core/services/notifications.hh similarity index 63% rename from src/core/modules/notifications.hh rename to src/runtime/core/services/notifications.hh index b2c8b2d77a..a32542ca26 100644 --- a/src/core/modules/notifications.hh +++ b/src/runtime/core/services/notifications.hh @@ -1,15 +1,17 @@ -#ifndef SOCKET_RUNTIME_CORE_MODULE_NOTIFICATIONS_H -#define SOCKET_RUNTIME_CORE_MODULE_NOTIFICATIONS_H +#ifndef SOCKET_RUNTIME_CORE_SERVICES_NOTIFICATIONS_H +#define SOCKET_RUNTIME_CORE_SERVICES_NOTIFICATIONS_H -#include "../module.hh" +#include "../../core.hh" -namespace SSC { - class CoreNotifications; +#include "permissions.hh" + +namespace ssc::runtime::core::services { + class Notifications; } #if SOCKET_RUNTIME_PLATFORM_APPLE @interface SSCUserNotificationCenterDelegate : NSObject -@property (nonatomic) SSC::CoreNotifications* notifications; +@property (nonatomic) ssc::runtime::core::services::Notifications* notifications; - (void) userNotificationCenter: (UNUserNotificationCenter*) center didReceiveNotificationResponse: (UNNotificationResponse*) response withCompletionHandler: (void (^)(void)) completionHandler; @@ -20,15 +22,15 @@ namespace SSC { @end #endif -namespace SSC { - class CoreNotifications : public CoreModule { +namespace ssc::runtime::core::services { + class Notifications : public core::Service { public: - using PermissionChangeObserver = CoreModule::Observer; - using PermissionChangeObservers = CoreModule::Observers; - using NotificationResponseObserver = CoreModule::Observer; - using NotificationResponseObservers = CoreModule::Observers; - using NotificationPresentedObserver = CoreModule::Observer; - using NotificationPresentedObservers = CoreModule::Observers; + using PermissionChangeObserver = Observer; + using PermissionChangeObservers = Observers; + using NotificationResponseObserver = Observer; + using NotificationResponseObservers = Observers; + using NotificationPresentedObserver = Observer; + using NotificationPresentedObservers = Observers; struct Notification { String identifier; @@ -55,8 +57,8 @@ namespace SSC { Notification notification; }; - using ShowCallback = Function; - using ListCallback = Function&)>; + using ShowCallback = Function; + using ListCallback = Function)>; #if SOCKET_RUNTIME_PLATFORM_APPLE SSCUserNotificationCenterDelegate* userNotificationCenterDelegate = nullptr; @@ -64,38 +66,40 @@ namespace SSC { UNAuthorizationStatus __block currentUserNotificationAuthorizationStatus; #endif + Permissions permissions; Mutex mutex; PermissionChangeObservers permissionChangeObservers; NotificationResponseObservers notificationResponseObservers; NotificationPresentedObservers notificationPresentedObservers; - CoreNotifications (Core* core); - ~CoreNotifications (); - void start (); - void stop (); + Notifications (const Options&); + ~Notifications (); + + bool start (); + bool stop (); bool removePermissionChangeObserver (const PermissionChangeObserver& observer); bool addPermissionChangeObserver ( const PermissionChangeObserver& observer, - const PermissionChangeObserver::Callback& callback + const PermissionChangeObserver::Callback callback ); bool removeNotificationResponseObserver (const NotificationResponseObserver& observer); bool addNotificationResponseObserver ( const NotificationResponseObserver& observer, - const NotificationResponseObserver::Callback& callback + const NotificationResponseObserver::Callback callback ); bool removeNotificationPresentedObserver (const NotificationPresentedObserver& observer); bool addNotificationPresentedObserver ( const NotificationPresentedObserver& observer, - const NotificationPresentedObserver::Callback& callback + const NotificationPresentedObserver::Callback callback ); - bool show (const ShowOptions& options, const ShowCallback& callback); + bool show (const ShowOptions& options, const ShowCallback callback); bool close (const Notification& notification); - void list (const ListCallback& callback) const; + void list (const ListCallback callback) const; }; } #endif diff --git a/src/runtime/core/services/os.cc b/src/runtime/core/services/os.cc new file mode 100644 index 0000000000..354fb9a868 --- /dev/null +++ b/src/runtime/core/services/os.cc @@ -0,0 +1,341 @@ +#include "../../string.hh" +#include "../../bytes.hh" +#include "../../json.hh" +#include "../../os.hh" + +#include "../services.hh" + +#include "os.hh" +#include "udp.hh" + +using ssc::runtime::bytes::toByteArray; + +namespace ssc::runtime::core::services { + void OS::cpus ( + const String& seq, + const Callback callback + ) const { + this->loop.dispatch([=, this]() { + #if SOCKET_RUNTIME_PLATFORM_ANDROID + { + auto json = JSON::Object::Entries { + {"source", "os.cpus"}, + {"data", JSON::Array::Entries {}} + }; + + callback(seq, json, QueuedResponse{}); + return; + } + #endif + + uv_cpu_info_t* infos = nullptr; + int count = 0; + int status = uv_cpu_info(&infos, &count); + + if (status != 0) { + auto json = JSON::Object::Entries { + {"source", "os.cpus"}, + {"err", JSON::Object::Entries { + {"message", uv_strerror(status)} + }} + }; + + callback(seq, json, QueuedResponse{}); + return; + } + + JSON::Array::Entries entries(count); + for (int i = 0; i < count; ++i) { + auto info = infos[i]; + entries[i] = JSON::Object::Entries { + {"model", info.model}, + {"speed", info.speed}, + {"times", JSON::Object::Entries { + {"user", info.cpu_times.user}, + {"nice", info.cpu_times.nice}, + {"sys", info.cpu_times.sys}, + {"idle", info.cpu_times.idle}, + {"irq", info.cpu_times.irq} + }} + }; + } + + auto json = JSON::Object::Entries { + {"source", "os.cpus"}, + {"data", entries} + }; + + uv_free_cpu_info(infos, count); + callback(seq, json, QueuedResponse{}); + }); + } + + void OS::networkInterfaces ( + const String& seq, + const Callback callback + ) const { + uv_interface_address_t *infos = nullptr; + StringStream value; + StringStream v4; + StringStream v6; + int count = 0; + int status = uv_interface_addresses(&infos, &count); + + if (status != 0) { + auto json = JSON::Object(JSON::Object::Entries { + {"source", "os.networkInterfaces"}, + {"err", JSON::Object::Entries { + {"type", "InternalError"}, + {"message", + String("Unable to get network interfaces: ") + String(uv_strerror(status)) + } + }} + }); + + return callback(seq, json, QueuedResponse{}); + } + + JSON::Object::Entries ipv4; + JSON::Object::Entries ipv6; + JSON::Object::Entries data; + + for (int i = 0; i < count; ++i) { + uv_interface_address_t info = infos[i]; + struct sockaddr_in *addr = (struct sockaddr_in*) &info.address.address4; + char mac[18] = {0}; + snprintf(mac, 18, "%02x:%02x:%02x:%02x:%02x:%02x", + (unsigned char) info.phys_addr[0], + (unsigned char) info.phys_addr[1], + (unsigned char) info.phys_addr[2], + (unsigned char) info.phys_addr[3], + (unsigned char) info.phys_addr[4], + (unsigned char) info.phys_addr[5] + ); + + if (addr->sin_family == AF_INET) { + JSON::Object::Entries entries; + entries["internal"] = info.is_internal == 0 ? "false" : "true"; + entries["address"] = udp::ip::addrToIPv4(addr); + entries["mac"] = String(mac, 17); + ipv4[String(info.name)] = entries; + } + + if (addr->sin_family == AF_INET6) { + JSON::Object::Entries entries; + entries["internal"] = info.is_internal == 0 ? "false" : "true"; + entries["address"] = udp::ip::addrToIPv6((struct sockaddr_in6*) addr); + entries["mac"] = String(mac, 17); + ipv6[String(info.name)] = entries; + } + } + + uv_free_interface_addresses(infos, count); + + data["ipv4"] = ipv4; + data["ipv6"] = ipv6; + + auto json = JSON::Object::Entries { + {"source", "os.networkInterfaces"}, + {"data", data} + }; + + callback(seq, json, QueuedResponse{}); + } + + void OS::rusage ( + const String& seq, + const Callback callback + ) const { + uv_rusage_t usage; + auto status = uv_getrusage(&usage); + + if (status != 0) { + auto json = JSON::Object::Entries { + {"source", "os.rusage"}, + {"err", JSON::Object::Entries { + {"message", uv_strerror(status)} + }} + }; + + callback(seq, json, QueuedResponse{}); + return; + } + + auto json = JSON::Object::Entries { + {"source", "os.rusage"}, + {"data", JSON::Object::Entries { + {"ru_maxrss", usage.ru_maxrss} + }} + }; + + callback(seq, json, QueuedResponse{}); + } + + void OS::uname ( + const String& seq, + const Callback callback + ) const { + uv_utsname_t uname; + auto status = uv_os_uname(&uname); + + if (status != 0) { + auto json = JSON::Object::Entries { + {"source", "os.uname"}, + {"err", JSON::Object::Entries { + {"message", uv_strerror(status)} + }} + }; + + callback(seq, json, QueuedResponse{}); + return; + } + + auto json = JSON::Object::Entries { + {"source", "os.uname"}, + {"data", JSON::Object::Entries { + {"sysname", uname.sysname}, + {"release", uname.release}, + {"version", uname.version}, + {"machine", uname.machine} + }} + }; + + callback(seq, json, QueuedResponse{}); + } + + void OS::uptime ( + const String& seq, + const Callback callback + ) const { + double uptime; + auto status = uv_uptime(&uptime); + + if (status != 0) { + auto json = JSON::Object::Entries { + {"source", "os.uptime"}, + {"err", JSON::Object::Entries { + {"message", uv_strerror(status)} + }} + }; + + callback(seq, json, QueuedResponse{}); + return; + } + + auto json = JSON::Object::Entries { + {"source", "os.uptime"}, + {"data", uptime * 1000} // in milliseconds + }; + + callback(seq, json, QueuedResponse{}); + } + + void OS::hrtime ( + const String& seq, + const Callback callback + ) const { + const auto hrtime = uv_hrtime(); + const auto bytes = toByteArray(hrtime); + const auto size = bytes.size(); + const auto body = new char[size]{0}; + const auto json = JSON::Object {}; + auto post = QueuedResponse {}; + post.body.reset(body); + post.length = size; + memcpy(body, bytes.data(), size); + callback(seq, json, post); + } + + void OS::availableMemory ( + const String& seq, + const Callback callback + ) const { + const auto memory = uv_get_available_memory(); + const auto bytes = toByteArray(memory); + const auto size = bytes.size(); + const auto body = new char[size]{0}; + const auto json = JSON::Object {}; + auto post = QueuedResponse {}; + post.body.reset(body); + post.length = size; + memcpy(body, bytes.data(), size); + callback(seq, json, post); + } + + void OS::bufferSize ( + const String& seq, + UDP::ID id, + size_t size, + int buffer, + const Callback callback + ) const { + if (buffer == 0) { + buffer = OS::SEND_BUFFER; + } else if (buffer == 1) { + buffer = OS::RECV_BUFFER; + } + + this->loop.dispatch([=, this]() { + auto socket = this->services.udp.getSocket(id); + + if (socket == nullptr) { + auto json = JSON::Object::Entries { + {"source", "bufferSize"}, + {"err", JSON::Object::Entries { + {"id", std::to_string(id)}, + {"code", "NOT_FOUND_ERR"}, + {"type", "NotFoundError"}, + {"message", "No socket with specified id"} + }} + }; + + callback(seq, json, QueuedResponse{}); + return; + } + + Lock lock(socket->mutex); + auto handle = (uv_handle_t*) &socket->handle; + auto err = buffer == RECV_BUFFER + ? uv_recv_buffer_size(handle, (int *) &size) + : uv_send_buffer_size(handle, (int *) &size); + + if (err < 0) { + auto json = JSON::Object::Entries { + {"source", "bufferSize"}, + {"err", JSON::Object::Entries { + {"id", std::to_string(id)}, + {"code", "NOT_FOUND_ERR"}, + {"type", "NotFoundError"}, + {"message", String(uv_strerror(err))} + }} + }; + + callback(seq, json, QueuedResponse{}); + return; + } + + auto json = JSON::Object::Entries { + {"source", "bufferSize"}, + {"data", JSON::Object::Entries { + {"id", std::to_string(id)}, + {"size", (int) size} + }} + }; + + callback(seq, json, QueuedResponse{}); + }); + } + + void OS::constants ( + const String& seq, + const Callback callback + ) const { + static const auto data = JSON::Object(os::constants()); + static const auto json = JSON::Object::Entries { + {"source", "os.constants"}, + {"data", data} + }; + + callback(seq, json, QueuedResponse {}); + } +} diff --git a/src/runtime/core/services/os.hh b/src/runtime/core/services/os.hh new file mode 100644 index 0000000000..3318be59c5 --- /dev/null +++ b/src/runtime/core/services/os.hh @@ -0,0 +1,28 @@ +#ifndef SOCKET_RUNTIME_CORE_SERVICES_OS_H +#define SOCKET_RUNTIME_CORE_SERVICES_OS_H + +#include "../../ipc.hh" +#include "../../core.hh" + +namespace ssc::runtime::core::services { + class OS : public core::Service { + public: + static const int RECV_BUFFER = 1; + static const int SEND_BUFFER = 0; + + OS (const Options& options) + : core::Service(options) + {} + + void cpus ( const ipc::Message::Seq&, const Callback) const; + void rusage (const ipc::Message::Seq&, const Callback) const; + void uname (const ipc::Message::Seq&, const Callback) const; + void uptime (const ipc::Message::Seq&, const Callback) const; + void hrtime (const ipc::Message::Seq&, const Callback) const; + void constants (const ipc::Message::Seq&, const Callback) const; + void bufferSize (const ipc::Message::Seq&, uint64_t, size_t, int /* RECV_BUFFER|SEND_BUFFER */, const Callback) const; + void availableMemory (const ipc::Message::Seq&, const Callback) const; + void networkInterfaces (const ipc::Message::Seq&, const Callback) const; + }; +} +#endif diff --git a/src/core/modules/permissions.cc b/src/runtime/core/services/permissions.cc similarity index 88% rename from src/core/modules/permissions.cc rename to src/runtime/core/services/permissions.cc index 50e26c77b3..b2746ea836 100644 --- a/src/core/modules/permissions.cc +++ b/src/runtime/core/services/permissions.cc @@ -1,8 +1,14 @@ -#include "../core.hh" +#include "../../debug.hh" +#include "../../config.hh" +#include "../../string.hh" + #include "permissions.hh" -namespace SSC { - bool CorePermissions::hasRuntimePermission (const String& permission) const { +using ssc::runtime::config::getUserConfig; +using ssc::runtime::string::replace; + +namespace ssc::runtime::core::services { + bool Permissions::hasRuntimePermission (const String& permission) const { static const auto userConfig = getUserConfig(); const auto key = String("permissions_allow_") + replace(permission, "-", "_"); @@ -13,12 +19,12 @@ namespace SSC { return userConfig.at(key) != "false"; } - void CorePermissions::query ( + void Permissions::query ( const String& seq, const String& name, - const Callback& callback + const Callback callback ) const { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { if (!this->hasRuntimePermission(name)) { JSON::Object json = JSON::Object::Entries { {"data", JSON::Object::Entries { @@ -26,7 +32,7 @@ namespace SSC { {"reason", "Runtime permission is disabled for '" + name + "'"} }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); return; } @@ -96,7 +102,7 @@ namespace SSC { } #endif - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); } if (name == "notifications") { @@ -121,12 +127,12 @@ namespace SSC { json["data"] = JSON::Object::Entries {{"state", "granted"}}; } - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); }]; #elif SOCKET_RUNTIME_PLATFORM_ANDROID const auto attachment = Android::JNIEnvironmentAttachment(this->core->platform.jvm); // `activity.checkPermission(permission)` - const auto hasPostNotifications = CallClassMethodFromAndroidEnvironment( + const auto hasQueuedResponseNotifications = CallClassMethodFromAndroidEnvironment( attachment.env, Boolean, this->core->platform.activity, @@ -144,7 +150,7 @@ namespace SSC { "()Z" ); - if (!hasPostNotifications || !isNotificationManagerEnabled) { + if (!hasQueuedResponseNotifications || !isNotificationManagerEnabled) { json = JSON::Object::Entries { {"data", JSON::Object::Entries { {"state", "prompt"}} @@ -158,9 +164,9 @@ namespace SSC { }; } - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); #else - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); #endif } @@ -192,14 +198,14 @@ namespace SSC { }; } - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); #else json = JSON::Object::Entries { {"data", JSON::Object::Entries { {"state", "prompt"}} } }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); #endif } @@ -231,33 +237,33 @@ namespace SSC { }; } - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); #else json = JSON::Object::Entries { {"data", JSON::Object::Entries { {"state", "prompt"}} } }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); #endif } }); } - void CorePermissions::request ( + void Permissions::request ( const String& seq, const String& name, - const Map& options, - const Callback& callback + const Map& options, + const Callback callback ) const { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { if (!this->hasRuntimePermission(name)) { JSON::Object json = JSON::Object::Entries { {"data", JSON::Object::Entries { {"state", "denied"}} } }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); return; } @@ -286,14 +292,14 @@ namespace SSC { json["data"] = JSON::Object::Entries {{"state", "denied"}}; } - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); }]; if (!performedActivation) { auto err = JSON::Object::Entries {{ "message", "Location observer could not be activated" }}; err["type"] = "GeolocationPositionError"; json["err"] = err; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } #elif SOCKET_RUNTIME_PLATFORM_ANDROID const auto attachment = Android::JNIEnvironmentAttachment(this->core->platform.jvm); @@ -337,12 +343,12 @@ namespace SSC { attachment.env->NewStringUTF("android.permission.ACCESS_FINE_LOCATION") ); - this->core->geolocation.addPermissionChangeObserver(observer, [seq, observer, callback, this](auto result) mutable { + this->core->geolocation.addPermissionChangeObserver(observer, [this, observer, callback, seq](auto result) mutable { JSON::Object json = JSON::Object::Entries { {"data", result} }; - callback(seq, json, Post{}); - this->core->dispatchEventLoop([=] () { + callback(seq, json, QueuedResponse{}); + this->loop.dispatch([this, observer] () { this->core->geolocation.removePermissionChangeObserver(observer); }); }); @@ -360,7 +366,7 @@ namespace SSC { {"state", "granted"}} } }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); } #endif } @@ -424,13 +430,13 @@ namespace SSC { json["data"] = JSON::Object::Entries {{"state", "granted"}}; } - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); }]; }]; #elif SOCKET_RUNTIME_PLATFORM_ANDROID const auto attachment = Android::JNIEnvironmentAttachment(this->core->platform.jvm); // `activity.checkPermission(permission)` - const auto hasPostNotifications = CallClassMethodFromAndroidEnvironment( + const auto hasQueuedResponseNotifications = CallClassMethodFromAndroidEnvironment( attachment.env, Boolean, this->core->platform.activity, @@ -448,7 +454,7 @@ namespace SSC { "()Z" ); - if (!hasPostNotifications || !isNotificationManagerEnabled) { + if (!hasQueuedResponseNotifications || !isNotificationManagerEnabled) { json = JSON::Object::Entries { {"data", JSON::Object::Entries { {"state", "prompt"}} @@ -462,7 +468,7 @@ namespace SSC { }; } - if (!hasPostNotifications || !isNotificationManagerEnabled) { + if (!hasQueuedResponseNotifications || !isNotificationManagerEnabled) { CoreNotifications::PermissionChangeObserver observer; auto permissions = attachment.env->NewObjectArray( 1, @@ -476,12 +482,12 @@ namespace SSC { attachment.env->NewStringUTF("android.permission.POST_NOTIFICATIONS") ); - this->core->notifications.addPermissionChangeObserver(observer, [=](auto result) mutable { + this->core->notifications.addPermissionChangeObserver(observer, [this, observer, callback, seq](auto result) mutable { JSON::Object json = JSON::Object::Entries { {"data", result} }; - callback(seq, json, Post{}); - this->core->dispatchEventLoop([=]() { + callback(seq, json, QueuedResponse{}); + this->loop.dispatch([this, observer]() { this->core->notifications.removePermissionChangeObserver(observer); }); }); @@ -499,10 +505,10 @@ namespace SSC { {"state", "granted"}} } }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); } #else - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); #endif } @@ -538,13 +544,13 @@ namespace SSC { attachment.env->NewStringUTF("android.permission.CAMERA") ); - this->core->mediaDevices.addPermissionChangeObserver(observer, [=](JSON::Object result) mutable { + this->core->mediaDevices.addPermissionChangeObserver(observer, [this, observer, callback, seq](JSON::Object result) mutable { if (result.get("name").str() == "camera") { JSON::Object json = JSON::Object::Entries { {"data", result} }; - callback(seq, json, Post{}); - this->core->dispatchEventLoop([=]() { + callback(seq, json, QueuedResponse{}); + this->loop.dispatch([this, observer]() { this->core->mediaDevices.removePermissionChangeObserver(observer); }); } @@ -563,10 +569,10 @@ namespace SSC { {"state", "granted"}} } }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); } #else - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); #endif } @@ -602,13 +608,13 @@ namespace SSC { attachment.env->NewStringUTF("android.permission.RECORD_AUDIO") ); - this->core->mediaDevices.addPermissionChangeObserver(observer, [=](JSON::Object result) mutable { + this->core->mediaDevices.addPermissionChangeObserver(observer, [this, observer, callback, seq](JSON::Object result) mutable { if (result.get("name").str() == "microphone") { JSON::Object json = JSON::Object::Entries { {"data", result} }; - callback(seq, json, Post{}); - this->core->dispatchEventLoop([=]() { + callback(seq, json, QueuedResponse{}); + this->loop.dispatch([this, observer]() { this->core->mediaDevices.removePermissionChangeObserver(observer); }); } @@ -627,10 +633,10 @@ namespace SSC { {"state", "granted"}} } }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); } #else - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); #endif } }); diff --git a/src/runtime/core/services/permissions.hh b/src/runtime/core/services/permissions.hh new file mode 100644 index 0000000000..67677fed53 --- /dev/null +++ b/src/runtime/core/services/permissions.hh @@ -0,0 +1,19 @@ +#ifndef SOCKET_RUNTIME_CORE_SERVICES_PERMISSIONS_H +#define SOCKET_RUNTIME_CORE_SERVICES_PERMISSIONS_H + +#include "../../ipc.hh" +#include "../../core.hh" + +namespace ssc::runtime::core::services { + class Permissions : public core::Service { + public: + Permissions (const Options& options) + : core::Service(options) + {} + + void query (const ipc::Message::Seq&, const String&, const Callback) const; + void request (const ipc::Message::Seq&, const String&, const Map&, const Callback) const; + bool hasRuntimePermission (const String& permission) const; + }; +} +#endif diff --git a/src/core/modules/platform.cc b/src/runtime/core/services/platform.cc similarity index 85% rename from src/core/modules/platform.cc rename to src/runtime/core/services/platform.cc index d3ff112636..13be55740b 100644 --- a/src/core/modules/platform.cc +++ b/src/runtime/core/services/platform.cc @@ -1,29 +1,20 @@ -#include "../process.hh" -#include "../codec.hh" -#include "../core.hh" -#include "../json.hh" +#include "../../process.hh" +#include "../../string.hh" +#include "../../json.hh" + #include "platform.hh" -namespace SSC { -#if SOCKET_RUNTIME_PLATFORM_ANDROID - void CorePlatform::configureAndroidContext ( - Android::JVMEnvironment jvm, - Android::Activity activity - ) { - this->jvm = jvm; - this->activity = activity; - this->contentResolver.activity = activity; - this->contentResolver.jvm = jvm; - } +using ssc::runtime::url::decodeURIComponent; +using ssc::runtime::process::exec; -#endif - void CorePlatform::event ( +namespace ssc::runtime::core::services { + void Platform::event ( const String& seq, const String& event, const String& data, const String& frameType, const String& frameSource, - const CoreModule::Callback& callback + const Callback callback ) { if (event == "domcontentloaded") { this->wasFirstDOMContentLoadedEventDispatched = true; @@ -34,13 +25,13 @@ namespace SSC { {"data", JSON::Object {}} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); } - void CorePlatform::revealFile ( + void Platform::revealFile ( const String& seq, const String& value, - const CoreModule::Callback& callback + const Callback callback ) const { String errorMessage = "Failed to open external file"; String pathToFile = decodeURIComponent(value); @@ -72,16 +63,16 @@ namespace SSC { json["data"] = JSON::Object {}; } - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); } - void CorePlatform::openExternal ( + void Platform::openExternal ( const String& seq, const String& value, - const CoreModule::Callback& callback + const Callback callback ) const { #if SOCKET_RUNTIME_PLATFORM_APPLE - this->core->dispatchEventLoop([=]() { + this->loop.dispatch([=]() { __block const auto url = [NSURL URLWithString: @(value.c_str())]; #if SOCKET_RUNTIME_PLATFORM_IOS @@ -101,7 +92,7 @@ namespace SSC { }; } - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); }]; #else auto workspace = [NSWorkspace sharedWorkspace]; @@ -129,7 +120,7 @@ namespace SSC { }; } - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); }]; #endif }); @@ -185,20 +176,20 @@ namespace SSC { g_list_free(list); } - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); #elif SOCKET_RUNTIME_PLATFORM_WINDOWS auto uri = value.c_str(); ShellExecute(nullptr, "Open", uri, nullptr, nullptr, SW_SHOWNORMAL); // TODO how to detect success here. do we care? - callback(seq, JSON::Object{}, Post{}); + callback(seq, JSON::Object{}, QueuedResponse{}); #elif SOCKET_RUNTIME_PLATFORM_ANDROID JSON::Object json; - const auto attachment = Android::JNIEnvironmentAttachment(this->jvm); + const auto attachment = Android::JNIEnvironmentAttachment(this->getRuntimeContext()->jvm); // `activity.openExternal(url)` CallClassMethodFromAndroidEnvironment( attachment.env, Boolean, - this->activity, + this->getRuntimeContext()->activity, "openExternal", "(Ljava/lang/String;)Z", attachment.env->NewStringUTF(value.c_str()) @@ -218,7 +209,7 @@ namespace SSC { }; } - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); #else const auto json = JSON::Object::Entries { {"source", "platform.openExternal"}, @@ -227,7 +218,7 @@ namespace SSC { {"message", "Operation not supported"} }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); #endif } } diff --git a/src/runtime/core/services/platform.hh b/src/runtime/core/services/platform.hh new file mode 100644 index 0000000000..bb25b15c31 --- /dev/null +++ b/src/runtime/core/services/platform.hh @@ -0,0 +1,28 @@ +#ifndef SOCKET_RUNTIME_CORE_SERVICES_PLATFORM_H +#define SOCKET_RUNTIME_CORE_SERVICES_PLATFORM_H + +#include "../../ipc.hh" +#include "../../core.hh" + +namespace ssc::runtime::core::services { + class Platform : public core::Service { + public: + Atomic wasFirstDOMContentLoadedEventDispatched = false; + + Platform (const Options& options) + : core::Service(options) + {} + + void event ( + const ipc::Message::Seq&, + const String&, + const String&, + const String&, + const String&, + const Callback + ); + void openExternal (const ipc::Message::Seq&, const String&, const Callback) const; + void revealFile ( const ipc::Message::Seq&, const String&, const Callback) const; + }; +} +#endif diff --git a/src/core/modules/child_process.cc b/src/runtime/core/services/process.cc similarity index 76% rename from src/core/modules/child_process.cc rename to src/runtime/core/services/process.cc index 4a993422ba..4ab762f2d0 100644 --- a/src/core/modules/child_process.cc +++ b/src/runtime/core/services/process.cc @@ -1,11 +1,16 @@ -#include "../headers.hh" -#include "../codec.hh" -#include "../core.hh" -#include "child_process.hh" -#include "timers.hh" - -namespace SSC { - void CoreChildProcess::shutdown () { +#include "../../process.hh" +#include "../../string.hh" +#include "../../http.hh" +#include "../../url.hh" + +#include "process.hh" + +using namespace ssc::runtime::string; +using ssc::runtime::http::Headers; +using ssc::runtime::url::encodeURIComponent; + +namespace ssc::runtime::core::services { + void Process::shutdown () { #if !SOCKET_RUNTIME_PLATFORM_IOS Lock lock(this->mutex); for (const auto& entry : this->handles) { @@ -18,11 +23,11 @@ namespace SSC { this->handles.clear(); } - void CoreChildProcess::kill ( + void Process::kill ( const String& seq, ID id, int signal, - const CoreModule::Callback& callback + const Callback callback ) { #if SOCKET_RUNTIME_PLATFORM_IOS const auto json = JSON::Object::Entries { @@ -32,9 +37,9 @@ namespace SSC { {"message", "kill() is not supported"} }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); #else - this->core->dispatchEventLoop([=, this] { + this->loop.dispatch([=, this] { Lock lock(this->mutex); if (!this->handles.contains(id)) { @@ -46,8 +51,8 @@ namespace SSC { }} }; - return this->core->dispatchEventLoop([=, this] () { - callback(seq, json, Post{}); + return this->loop.dispatch([=, this] () { + callback(seq, json, QueuedResponse{}); }); } @@ -59,19 +64,19 @@ namespace SSC { ::kill(-process->id, signal); #endif - return this->core->dispatchEventLoop([=, this] () { - callback(seq, JSON::Object{}, Post{}); + return this->loop.dispatch([=, this] () { + callback(seq, JSON::Object{}, QueuedResponse{}); }); }); #endif } - void CoreChildProcess::exec ( + void Process::exec ( const String& seq, ID id, const Vector args, const ExecOptions options, - const CoreModule::Callback& callback + const Callback callback ) { #if SOCKET_RUNTIME_PLATFORM_IOS const auto json = JSON::Object::Entries { @@ -81,9 +86,9 @@ namespace SSC { {"message", "exec() is not supported"} }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); #else - this->core->dispatchEventLoop([=, this] { + this->loop.dispatch([=, this] { Lock lock(this->mutex); if (this->handles.contains(id)) { @@ -94,16 +99,22 @@ namespace SSC { }} }; - return this->core->dispatchEventLoop([=, this] () { - callback(seq, json, Post{}); + this->loop.dispatch([=, this] () { + callback(seq, json, QueuedResponse{}); }); + return; } - SharedPointer process = nullptr; - CoreTimers::ID timer; + SharedPointer process = nullptr; + Timers::ID timer; const auto command = args.size() > 0 ? args.at(0) : String(""); - const auto argv = join(args.size() > 1 ? Vector{ args.begin() + 1, args.end() } : Vector{}, " "); + const auto argv = join( + args.size() > 1 + ? Vector{ args.begin() + 1, args.end() } + : Vector{}, + " " + ); auto stdoutBuffer = new StringStream; auto stderrBuffer = new StringStream; @@ -130,10 +141,10 @@ namespace SSC { const auto onExit = [=, this](const String& output) mutable { if (timer > 0) { - this->core->clearTimeout(timer); + this->timers.clearTimeout(timer); } - this->core->dispatchEventLoop([=, this] () mutable { + this->loop.dispatch([=, this] () mutable { Lock lock(this->mutex); if (this->handles.contains(id)) { auto process = this->handles.at(id); @@ -156,8 +167,8 @@ namespace SSC { stdoutBuffer = nullptr; stderrBuffer = nullptr; - return this->core->dispatchEventLoop([=, this] () { - callback(seq, json, Post{}); + this->loop.dispatch([=, this] () { + callback(seq, json, QueuedResponse{}); }); this->handles.erase(id); @@ -165,7 +176,7 @@ namespace SSC { }); }; - process.reset(new Process( + process.reset(new runtime::Process( command, argv, options.env, @@ -181,7 +192,7 @@ namespace SSC { const auto pid = process->open(); if (options.timeout > 0) { - timer = this->core->setTimeout(options.timeout, [=, this] () mutable { + timer = this->timers.setTimeout(options.timeout, [=, this] () mutable { Lock lock(this->mutex); const auto json = JSON::Object::Entries { {"source", "child_process.exec"}, @@ -194,8 +205,8 @@ namespace SSC { }} }; - this->core->dispatchEventLoop([=, this] { - callback(seq, json, Post{}); + this->loop.dispatch([=, this] { + callback(seq, json, QueuedResponse{}); }); #if SOCKET_RUNTIME_PLATFORM_WINDOWS @@ -217,12 +228,12 @@ namespace SSC { #endif } - void CoreChildProcess::spawn ( + void Process::spawn ( const String& seq, ID id, const Vector args, const SpawnOptions options, - const CoreModule::Callback& callback + const Callback callback ) { #if SOCKET_RUNTIME_PLATFORM_IOS const auto json = JSON::Object::Entries { @@ -232,9 +243,9 @@ namespace SSC { {"message", "spawn() is not supported"} }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); #else - this->core->dispatchEventLoop([=, this] { + this->loop.dispatch([=, this] { Lock lock(this->mutex); if (this->handles.contains(id)) { @@ -245,15 +256,20 @@ namespace SSC { }} }; - return this->core->dispatchEventLoop([=, this] () { - callback(seq, json, Post{}); + return this->loop.dispatch([=, this] () { + callback(seq, json, QueuedResponse{}); }); } - SharedPointer process = nullptr; + SharedPointer process = nullptr; const auto command = args.size() > 0 ? args.at(0) : String(""); - const auto argv = join(args.size() > 1 ? Vector{ args.begin() + 1, args.end() } : Vector{}, " "); + const auto argv = join( + args.size() > 1 + ? Vector{ args.begin() + 1, args.end() } + : Vector{}, + " " + ); const auto onStdout = [=](const String& output) { if (!options.allowStdout || output.size() == 0) { @@ -268,7 +284,7 @@ namespace SSC { memcpy(bytes, output.c_str(), output.size()); - Post post; + QueuedResponse post; post.id = rand64(); post.body.reset(bytes); post.length = (int) output.size(); @@ -298,7 +314,7 @@ namespace SSC { memcpy(bytes, output.c_str(), output.size()); - Post post; + QueuedResponse post; post.id = rand64(); post.body.reset(bytes); post.length = (int) output.size(); @@ -326,10 +342,10 @@ namespace SSC { }} }; - callback("-1", json, Post{}); + callback("-1", json, QueuedResponse{}); - this->core->dispatchEventLoop([=, this] { - SharedPointer process = nullptr; + this->loop.dispatch([=, this] { + SharedPointer process = nullptr; do { Lock lock(this->mutex); if (!this->handles.contains(id)) { @@ -341,7 +357,7 @@ namespace SSC { const auto code = process->wait(); - this->core->dispatchEventLoop([=, this] { + this->loop.dispatch([=, this] { const auto json = JSON::Object::Entries { {"source", "child_process.spawn"}, {"data", JSON::Object::Entries { @@ -351,7 +367,7 @@ namespace SSC { }} }; - callback("-1", json, Post{}); + callback("-1", json, QueuedResponse{}); Lock lock(this->mutex); this->handles.erase(id); @@ -359,7 +375,7 @@ namespace SSC { }); }; - process.reset(new Process( + process.reset(new runtime::Process( command, argv, options.env, @@ -381,19 +397,19 @@ namespace SSC { }} }; - return this->core->dispatchEventLoop([=, this] () { - callback(seq, json, Post{}); + return this->loop.dispatch([=, this] () { + callback(seq, json, QueuedResponse{}); }); }); #endif } - void CoreChildProcess::write ( + void Process::write ( const String& seq, ID id, SharedPointer buffer, size_t size, - const CoreModule::Callback& callback + const Callback callback ) { #if SOCKET_RUNTIME_PLATFORM_IOS const auto json = JSON::Object::Entries { @@ -403,9 +419,9 @@ namespace SSC { {"message", "write() is not supported"} }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); #else - this->core->dispatchEventLoop([=, this] { + this->loop.dispatch([=, this] { Lock lock(this->mutex); if (!this->handles.contains(id)) { @@ -417,7 +433,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } bool didWrite = false; @@ -433,7 +449,7 @@ namespace SSC { }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); return; } @@ -448,7 +464,7 @@ namespace SSC { }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); return; } @@ -456,16 +472,22 @@ namespace SSC { const auto json = JSON::Object::Entries { {"err", JSON::Object::Entries { {"id", std::to_string(id)}, - {"type", process->lastWriteStatus != 0 ? "ErrnoError" : "InternalError"}, - {"message", process->lastWriteStatus != 0 ? strerror(process->lastWriteStatus) : "Failed to write to child process"} + {"type", process->lastWriteStatus != 0 + ? "ErrnoError" + : "InternalError" + }, + {"message", process->lastWriteStatus != 0 + ? strerror(process->lastWriteStatus) + : "Failed to write to child process" + } }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); return; } - callback(seq, JSON::Object{}, Post{}); + callback(seq, JSON::Object{}, QueuedResponse{}); return; }); #endif diff --git a/src/runtime/core/services/process.hh b/src/runtime/core/services/process.hh new file mode 100644 index 0000000000..6feb80acbb --- /dev/null +++ b/src/runtime/core/services/process.hh @@ -0,0 +1,54 @@ +#ifndef SOCKET_RUNTIME_CORE_SERVICES_PROCESS_H +#define SOCKET_RUNTIME_CORE_SERVICES_PROCESS_H + +#include "../../ipc.hh" +#include "../../core.hh" +#include "../../process.hh" +#include "../../queued_response.hh" + +#include "timers.hh" + +namespace ssc::runtime::core::services { + class Process : public core::Service { + public: + using ID = uint64_t; + using Handles = Map>; + + struct SpawnOptions { + String cwd; + const Vector env; + bool allowStdin = true; + bool allowStdout = true; + bool allowStderr = true; + }; + + struct ExecOptions { + String cwd; + const Vector env; + bool allowStdout = true; + bool allowStderr = true; + uint64_t timeout = 0; + #if SOCKET_RUNTIME_PLATFORM_WINDOWS || SOCKET_RUNTIME_PLATFORM_IOS + int killSignal = 0; // unused + #else + int killSignal = SIGTERM; + #endif + }; + + Handles handles; + Timers timers; + Mutex mutex; + + Process (const Options& options) + : core::Service(options), + timers(options) + {} + + void shutdown (); + void exec (const ipc::Message::Seq&, ID, const Vector, const ExecOptions, const Callback); + void spawn (const ipc::Message::Seq&, ID, const Vector, const SpawnOptions, const Callback); + void kill (const ipc::Message::Seq&, ID, int, const Callback); + void write (const ipc::Message::Seq&, ID, SharedPointer, size_t, const Callback); + }; +} +#endif diff --git a/src/core/modules/timers.cc b/src/runtime/core/services/timers.cc similarity index 63% rename from src/core/modules/timers.cc rename to src/runtime/core/services/timers.cc index 66e861d3c2..a08c384d58 100644 --- a/src/core/modules/timers.cc +++ b/src/runtime/core/services/timers.cc @@ -1,22 +1,26 @@ -#include "../core.hh" #include "timers.hh" -namespace SSC { - CoreTimers::Timer::Timer (CoreTimers* timers, ID id, Callback callback) +namespace ssc::runtime::core::services { + struct TimerToken { + Timers* timers = nullptr; + Timers::ID id = 0; + }; + + Timers::Timer::Timer (Timers* timers, ID id, Callback callback) : timers(timers), id(id), callback(callback) {} - const CoreTimers::ID CoreTimers::createTimer ( + const Timers::ID Timers::createTimer ( uint64_t timeout, uint64_t interval, - const Callback& callback + const Callback callback ) { Lock lock(this->mutex); auto id = rand64(); - auto loop = this->core->getEventLoop(); + auto loop = this->loop.get(); auto handle = std::make_shared( this, id, @@ -29,49 +33,53 @@ namespace SSC { this->handles.emplace(handle->id, handle); - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { Lock lock(this->mutex); if (this->handles.contains(id)) { auto handle = this->handles.at(id); - uv_handle_set_data((uv_handle_t*) &handle->timer, (void*) id); + const auto token = new TimerToken{ this, id }; + uv_handle_set_data((uv_handle_t*) &handle->timer, (void*) token); uv_timer_init(loop, &handle->timer); uv_timer_start( &handle->timer, [](uv_timer_t* timer) { - auto loop = uv_handle_get_loop(reinterpret_cast(timer)); - auto core = reinterpret_cast(uv_loop_get_data(loop)); - auto id = reinterpret_cast(uv_handle_get_data(reinterpret_cast(timer))); + const auto token = reinterpret_cast(uv_handle_get_data(reinterpret_cast(timer))); // bad state - if (core == nullptr) { + if (token == nullptr) { uv_timer_stop(timer); return; } - Lock lock(core->timers.mutex); + Lock lock(token->timers->mutex); + const auto id = token->id; // cancelled (removed from 'handles') - if (!core->timers.handles.contains(id)) { + if (!token->timers->handles.contains(id)) { + delete token; uv_timer_stop(timer); return; } - auto handle = core->timers.handles.at(id); + auto handle = token->timers->handles.at(id); // bad ref if (handle == nullptr) { + delete token; uv_timer_stop(timer); return; } // `callback` to timer callback is a "cancel" function handle->callback([=] () { - core->timers.cancelTimer(id); + token->timers->cancelTimer(id); + delete token; }); if (!handle->repeat) { - if (core->timers.handles.contains(id)) { - core->timers.handles.erase(id); + if (token->timers->handles.contains(id)) { + token->timers->handles.erase(id); + delete token; } } }, @@ -84,7 +92,7 @@ namespace SSC { return id; } - bool CoreTimers::cancelTimer (const ID id) { + bool Timers::cancelTimer (const ID id) { Lock lock(this->mutex); if (!this->handles.contains(id)) { @@ -98,9 +106,9 @@ namespace SSC { return true; } - const CoreTimers::ID CoreTimers::setTimeout ( + const Timers::ID Timers::setTimeout ( uint64_t timeout, - const TimeoutCallback& callback + const TimeoutCallback callback ) { Lock lock(this->mutex); const auto id = this->createTimer(timeout, 0, [callback] (auto _) { @@ -114,13 +122,13 @@ namespace SSC { return id; } - bool CoreTimers::clearTimeout (const ID id) { + bool Timers::clearTimeout (const ID id) { return this->cancelTimer(id); } - const CoreTimers::ID CoreTimers::setInterval ( + const Timers::ID Timers::setInterval ( uint64_t interval, - const IntervalCallback& callback + const IntervalCallback callback ) { Lock lock(this->mutex); @@ -133,11 +141,11 @@ namespace SSC { return id; } - bool CoreTimers::clearInterval (const ID id) { + bool Timers::clearInterval (const ID id) { return this->cancelTimer(id); } - const CoreTimers::ID CoreTimers::setImmediate (const ImmediateCallback& callback) { + const Timers::ID Timers::setImmediate (const ImmediateCallback callback) { Lock lock(this->mutex); const auto id = this->createTimer(0, 0, [callback] (auto _) { @@ -151,7 +159,7 @@ namespace SSC { return id; } - bool CoreTimers::clearImmediate (const ID id) { + bool Timers::clearImmediate (const ID id) { return this->clearTimeout(id); } } diff --git a/src/core/modules/timers.hh b/src/runtime/core/services/timers.hh similarity index 50% rename from src/core/modules/timers.hh rename to src/runtime/core/services/timers.hh index ce4f4bf0c2..357854b261 100644 --- a/src/core/modules/timers.hh +++ b/src/runtime/core/services/timers.hh @@ -1,11 +1,10 @@ -#ifndef SOCKET_RUNTIME_CORE_MODULE_TIMERS_H -#define SOCKET_RUNTIME_CORE_MODULE_TIMERS_H +#ifndef SOCKET_RUNTIME_CORE_SERVICES_TIMERS_H +#define SOCKET_RUNTIME_CORE_SERVICES_TIMERS_H -#include "../module.hh" +#include "../../core.hh" -namespace SSC { - class Core; - class CoreTimers : public CoreModule { +namespace ssc::runtime::core::services { + class Timers : public core::Service { public: using ID = uint64_t; using CancelCallback = Function; @@ -17,39 +16,34 @@ namespace SSC { struct Timer { enum class Type { Timeout, Interval, Immediate }; - CoreTimers* timers = nullptr; + Timers* timers = nullptr; ID id = 0; Callback callback = nullptr; bool repeat = false; bool cancelled = false; uv_timer_t timer; Type type; - Timer (CoreTimers* timers, ID id, Callback callback); + Timer (Timers* timers, ID id, Callback callback); }; - using Handles = std::map>; + using Handles = Map>; Handles handles; Mutex mutex; - CoreTimers (Core* core) - : CoreModule (core) + Timers (const Options& options) + : core::Service(options) {} - const ID createTimer ( - uint64_t timeout, - uint64_t interval, - const Callback& callback - ); - - const ID setTimeout (uint64_t timeout, const TimeoutCallback& callback); - const ID setInterval (uint64_t interval, const IntervalCallback& callback); - const ID setImmediate (const ImmediateCallback& callback); + const ID setTimeout (uint64_t, const TimeoutCallback); + const ID setInterval (uint64_t, const IntervalCallback); + const ID setImmediate (const ImmediateCallback); bool cancelTimer (const ID id); bool clearTimeout (const ID id); bool clearInterval (const ID id); bool clearImmediate (const ID id); + const ID createTimer (uint64_t, uint64_t, const Callback); }; } #endif diff --git a/src/core/modules/udp.cc b/src/runtime/core/services/udp.cc similarity index 71% rename from src/core/modules/udp.cc rename to src/runtime/core/services/udp.cc index 01e6b60a2b..d112cdbb24 100644 --- a/src/core/modules/udp.cc +++ b/src/runtime/core/services/udp.cc @@ -1,12 +1,11 @@ -#include "../headers.hh" -#include "../core.hh" -#include "../ip.hh" +#include "../../http.hh" +#include "../../core.hh" #include "udp.hh" -namespace SSC { +namespace ssc::runtime::core::services { static JSON::Object::Entries ERR_SOCKET_ALREADY_BOUND ( const String& source, - CoreUDP::ID id + UDP::ID id ) { return JSON::Object::Entries { {"source", source}, @@ -21,7 +20,7 @@ namespace SSC { static JSON::Object::Entries ERR_SOCKET_DGRAM_IS_CONNECTED ( const String& source, - CoreUDP::ID id + UDP::ID id ) { return JSON::Object::Entries { {"source", source}, @@ -36,7 +35,7 @@ namespace SSC { static JSON::Object::Entries ERR_SOCKET_DGRAM_NOT_CONNECTED ( const String& source, - CoreUDP::ID id + UDP::ID id ) { return JSON::Object::Entries { {"source", source}, @@ -51,7 +50,7 @@ namespace SSC { static JSON::Object::Entries ERR_SOCKET_DGRAM_CLOSED ( const String& source, - CoreUDP::ID id + UDP::ID id ) { return JSON::Object::Entries { {"source", source}, @@ -66,7 +65,7 @@ namespace SSC { static JSON::Object::Entries ERR_SOCKET_DGRAM_CLOSING ( const String& source, - CoreUDP::ID id + UDP::ID id ) { return JSON::Object::Entries { {"source", source}, @@ -81,7 +80,7 @@ namespace SSC { static JSON::Object::Entries ERR_SOCKET_DGRAM_NOT_RUNNING ( const String& source, - CoreUDP::ID id + UDP::ID id ) { return JSON::Object::Entries { {"source", source}, @@ -94,96 +93,57 @@ namespace SSC { }; } - void CoreUDP::resumeAllSockets () { - this->core->dispatchEventLoop([=, this]() { - for (auto const &tuple : this->sockets) { - auto socket = tuple.second; - if (socket != nullptr && (socket->isBound() || socket->isConnected())) { - socket->resume(); - } - } - }); + void UDP::resumeAllSockets () { + this->manager.resume(); } - void CoreUDP::pauseAllSockets () { - for (auto const &tuple : this->sockets) { - auto socket = tuple.second; - if (socket != nullptr && (socket->isBound() || socket->isConnected())) { - socket->pause(); - } - } + void UDP::pauseAllSockets () { + this->manager.pause(); } - bool CoreUDP::hasSocket (ID id) { - Lock lock(this->mutex); - return this->sockets.find(id) != this->sockets.end(); + bool UDP::hasSocket (ID id) { + return this->manager.has(id); } - void CoreUDP::removeSocket (ID id) { - return this->removeSocket(id, false); + void UDP::removeSocket (ID id) { + return this->manager.remove(id, false); } - void CoreUDP::removeSocket (ID id, bool autoClose) { - if (this->hasSocket(id)) { - if (autoClose) { - auto socket = this->getSocket(id); - if (socket != nullptr) { - socket->close(); - } - } - - Lock lock(this->mutex); - this->sockets.erase(id); - } + void UDP::removeSocket (ID id, bool autoClose) { + return this->manager.remove(id, autoClose); } - SharedPointer CoreUDP::getSocket (ID id) { - if (!this->hasSocket(id)) return nullptr; - Lock lock(this->mutex); - return this->sockets.at(id); + SharedPointer UDP::getSocket (ID id) { + return this->manager.get(id); } - SharedPointer CoreUDP::createSocket (socket_type_t socketType, ID id) { + SharedPointer UDP::createSocket (udp::socket_type_t socketType, ID id) { return this->createSocket(socketType, id, false); } - SharedPointer CoreUDP::createSocket ( - socket_type_t socketType, + SharedPointer UDP::createSocket ( + udp::socket_type_t socketType, ID id, bool isEphemeral ) { - if (this->hasSocket(id)) { - auto socket = this->getSocket(id); - if (socket != nullptr) { - if (isEphemeral) { - Lock lock(socket->mutex); - socket->flags = (socket_flag_t) (socket->flags | SOCKET_FLAG_EPHEMERAL); - } - } - - return socket; - } - - Lock lock(this->mutex); - this->sockets[id].reset(new Socket(this->core, socketType, id, isEphemeral)); - return this->sockets.at(id); + return this->manager.create(socketType, id, isEphemeral); } - void CoreUDP::bind ( + void UDP::bind ( const String& seq, ID id, - const CoreUDP::BindOptions& options, - const CoreModule::Callback& callback + const UDP::BindOptions& options, + const Callback callback ) { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { if (this->hasSocket(id)) { if (this->getSocket(id)->isBound()) { auto json = ERR_SOCKET_ALREADY_BOUND("udp.bind", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } } - auto socket = this->createSocket(SOCKET_TYPE_UDP, id); + auto socket = this->createSocket(udp::SOCKET_TYPE_UDP, id); auto err = socket->bind(options.address, options.port, options.reuseAddr); if (err < 0) { @@ -195,7 +155,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto info = socket->getLocalPeerInfo(); @@ -209,7 +169,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto json = JSON::Object::Entries { @@ -223,22 +183,22 @@ namespace SSC { }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); }); } - void CoreUDP::connect ( + void UDP::connect ( const String& seq, ID id, - const CoreUDP::ConnectOptions& options, - const CoreModule::Callback& callback + const UDP::ConnectOptions& options, + const Callback callback ) { - this->core->dispatchEventLoop([=, this]() { - auto socket = this->createSocket(SOCKET_TYPE_UDP, id); + this->loop.dispatch([=, this]() { + auto socket = this->createSocket(udp::SOCKET_TYPE_UDP, id); if (socket->isConnected()) { auto json = ERR_SOCKET_DGRAM_IS_CONNECTED("udp.connect", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto err = socket->connect(options.address, options.port); @@ -252,7 +212,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto info = socket->getRemotePeerInfo(); @@ -266,7 +226,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto json = JSON::Object::Entries { @@ -279,19 +239,19 @@ namespace SSC { }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); }); } - void CoreUDP::disconnect ( + void UDP::disconnect ( const String& seq, ID id, - const CoreModule::Callback& callback + const Callback callback ) { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { if (!this->hasSocket(id)) { auto json = ERR_SOCKET_DGRAM_NOT_CONNECTED("udp.disconnect", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto socket = this->getSocket(id); @@ -306,7 +266,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto json = JSON::Object::Entries { @@ -316,18 +276,18 @@ namespace SSC { }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); }); } - void CoreUDP::getPeerName ( + void UDP::getPeerName ( const String& seq, ID id, - const CoreModule::Callback& callback + const Callback callback ) { if (!this->hasSocket(id)) { auto json = ERR_SOCKET_DGRAM_NOT_CONNECTED("udp.getPeerName", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto socket = this->getSocket(id); @@ -342,7 +302,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto json = JSON::Object::Entries { @@ -355,17 +315,17 @@ namespace SSC { }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); } - void CoreUDP::getSockName ( + void UDP::getSockName ( const String& seq, ID id, - const Callback& callback + const Callback callback ) { if (!this->hasSocket(id)) { auto json = ERR_SOCKET_DGRAM_NOT_RUNNING("udp.getSockName", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto socket = this->getSocket(id); @@ -380,7 +340,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto json = JSON::Object::Entries { @@ -393,24 +353,24 @@ namespace SSC { }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); } - void CoreUDP::getState ( + void UDP::getState ( const String& seq, ID id, - const CoreModule::Callback& callback + const Callback callback ) { if (!this->hasSocket(id)) { auto json = ERR_SOCKET_DGRAM_NOT_RUNNING("udp.getState", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto socket = this->getSocket(id); if (!socket->isUDP()) { auto json = ERR_SOCKET_DGRAM_NOT_RUNNING("udp.getState", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto json = JSON::Object::Entries { @@ -427,17 +387,17 @@ namespace SSC { }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); } - void CoreUDP::send ( + void UDP::send ( const String& seq, ID id, - const CoreUDP::SendOptions& options, - const CoreModule::Callback& callback + const UDP::SendOptions& options, + const Callback callback ) { - this->core->dispatchEventLoop([=, this] { - auto socket = this->createSocket(SOCKET_TYPE_UDP, id, options.ephemeral); + this->loop.dispatch([=, this] { + auto socket = this->createSocket(udp::SOCKET_TYPE_UDP, id, options.ephemeral); auto size = options.size; // @TODO(jwerle): validate MTU auto port = options.port; auto bytes = options.bytes; @@ -452,7 +412,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto json = JSON::Object::Entries { @@ -463,30 +423,30 @@ namespace SSC { }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); }); }); } - void CoreUDP::readStart (const String& seq, ID id, const CoreModule::Callback& callback) { + void UDP::readStart (const String& seq, ID id, const Callback callback) { if (!this->hasSocket(id)) { auto json = ERR_SOCKET_DGRAM_NOT_RUNNING("udp.readStart", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto socket = this->getSocket(id); if (socket->isClosed()) { auto json = ERR_SOCKET_DGRAM_CLOSED("udp.readStart", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } if (socket->isClosing()) { auto json = ERR_SOCKET_DGRAM_CLOSING("udp.readStart", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } - if (socket->hasState(SOCKET_STATE_UDP_RECV_STARTED)) { + if (socket->hasState(udp::SOCKET_STATE_UDP_RECV_STARTED)) { auto json = JSON::Object::Entries { {"source", "udp.readStart"}, {"err", JSON::Object::Entries { @@ -495,7 +455,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } if (socket->isActive()) { @@ -506,7 +466,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto err = socket->recvstart([=](auto nread, auto buf, auto addr) { @@ -519,15 +479,15 @@ namespace SSC { }} }; - callback("-1", json, Post{}); + callback("-1", json, QueuedResponse{}); } else if (nread > 0 && buf && buf->base) { char address[17] = {0}; - Post post; + QueuedResponse post; int port; - IP::parseAddress((struct sockaddr *) addr, &port, address); + udp::ip::parseAddress((struct sockaddr *) addr, &port, address); - const auto headers = Headers {{ + const auto headers = http::Headers {{ {"content-type" ,"application/octet-stream"}, {"content-length", nread} }}; @@ -562,7 +522,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto json = JSON::Object::Entries { @@ -572,33 +532,33 @@ namespace SSC { }} }; - callback(seq, json, Post {}); + callback(seq, json, QueuedResponse {}); } - void CoreUDP::readStop ( + void UDP::readStop ( const String& seq, ID id, - const CoreModule::Callback& callback + const Callback callback ) { - this->core->dispatchEventLoop([=, this] { + this->loop.dispatch([=, this] { if (!this->hasSocket(id)) { auto json = ERR_SOCKET_DGRAM_NOT_RUNNING("udp.readStop", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto socket = this->getSocket(id); if (socket->isClosed()) { auto json = ERR_SOCKET_DGRAM_CLOSED("udp.readStop", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } if (socket->isClosing()) { auto json = ERR_SOCKET_DGRAM_CLOSING("udp.readStop", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } - if (!socket->hasState(SOCKET_STATE_UDP_RECV_STARTED)) { + if (!socket->hasState(udp::SOCKET_STATE_UDP_RECV_STARTED)) { auto json = JSON::Object::Entries { {"source", "udp.readStop"}, {"err", JSON::Object::Entries { @@ -607,7 +567,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto err = socket->recvstop(); @@ -621,7 +581,7 @@ namespace SSC { }} }; - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto json = JSON::Object::Entries { @@ -631,36 +591,36 @@ namespace SSC { }} }; - callback(seq, json, Post {}); + callback(seq, json, QueuedResponse {}); }); } - void CoreUDP::close ( + void UDP::close ( const String& seq, ID id, - const CoreModule::Callback& callback + const Callback callback ) { - this->core->dispatchEventLoop([=, this]() { + this->loop.dispatch([=, this]() { if (!this->hasSocket(id)) { auto json = ERR_SOCKET_DGRAM_NOT_RUNNING("udp.close", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } auto socket = this->getSocket(id); if (!socket->isUDP()) { auto json = ERR_SOCKET_DGRAM_NOT_RUNNING("udp.close", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } if (socket->isClosed()) { auto json = ERR_SOCKET_DGRAM_CLOSED("udp.close", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } if (socket->isClosing()) { auto json = ERR_SOCKET_DGRAM_CLOSING("udp.close", id); - return callback(seq, json, Post{}); + return callback(seq, json, QueuedResponse{}); } socket->close([=, this]() { @@ -671,8 +631,26 @@ namespace SSC { }} }; - callback(seq, json, Post{}); + callback(seq, json, QueuedResponse{}); }); }); } + + bool UDP::start () { + if (this->enabled) { + this->resumeAllSockets(); + return true; + } + + return false; + } + + bool UDP::stop () { + if (this->enabled) { + this->pauseAllSockets(); + return true; + } + + return false; + } } diff --git a/src/runtime/core/services/udp.hh b/src/runtime/core/services/udp.hh new file mode 100644 index 0000000000..5add263486 --- /dev/null +++ b/src/runtime/core/services/udp.hh @@ -0,0 +1,62 @@ +#ifndef SOCKET_RUNTIME_CORE_SERVICES_UDP_H +#define SOCKET_RUNTIME_CORE_SERVICES_UDP_H + +#include "../../core.hh" +#include "../../udp.hh" +#include "../../ipc.hh" + +namespace ssc::runtime::core::services { + class UDP : public core::Service { + public: + using ID = uint64_t; + + struct BindOptions { + String address; + int port; + bool reuseAddr = false; + }; + + struct ConnectOptions { + String address; + int port; + }; + + struct SendOptions { + String address = ""; + int port = 0; + SharedPointer bytes = nullptr; + size_t size = 0; + bool ephemeral = false; + }; + + Mutex mutex; + udp::SocketManager manager; + + UDP (const Options& options) + : core::Service(options), + manager({ options.loop }) + {} + + void bind (const ipc::Message::Seq&, ID, const BindOptions&, const Callback); + void close (const ipc::Message::Seq&, ID, const Callback); + void connect (const ipc::Message::Seq&, ID, const ConnectOptions&, const Callback); + void disconnect (const ipc::Message::Seq&, ID, const Callback); + void getPeerName (const ipc::Message::Seq&, ID, const Callback); + void getSockName (const ipc::Message::Seq&, ID, const Callback); + void getState (const ipc::Message::Seq&, ID, const Callback); + void readStart (const ipc::Message::Seq&, ID, const Callback); + void readStop (const ipc::Message::Seq&, ID, const Callback); + void send (const ipc::Message::Seq&, ID, const SendOptions& options, const Callback); + bool start (); + bool stop (); + void resumeAllSockets (); + void pauseAllSockets (); + bool hasSocket (ID); + void removeSocket (ID); + void removeSocket (ID, bool autoClose); + SharedPointer getSocket (ID); + SharedPointer createSocket (udp::socket_type_t type, ID); + SharedPointer createSocket (udp::socket_type_t type, ID, bool isEphemeral); + }; +} +#endif diff --git a/src/runtime/crypto.hh b/src/runtime/crypto.hh new file mode 100644 index 0000000000..71fed48137 --- /dev/null +++ b/src/runtime/crypto.hh @@ -0,0 +1,60 @@ +#ifndef SOCKET_RUNTIME_CRYPTO_H +#define SOCKET_RUNTIME_CRYPTO_H + +#include "platform.hh" + +#define SHA1_DIGEST_SIZE 20 + +namespace ssc::runtime::crypto { + uint64_t rand64 (); + + struct SHA1Context { + uint32_t state[5]; + uint64_t bitCount; + unsigned char buffer[64]; + }; + + void sha1_init (SHA1Context *ctx); + void sha1_update ( + SHA1Context *ctx, + const unsigned char *data, + size_t size + ); + + void sha1_final ( + SHA1Context *ctx, + unsigned char out[SHA1_DIGEST_SIZE] + ); + + void sha1 ( + const unsigned char *data, + size_t size, + unsigned char out[SHA1_DIGEST_SIZE] + ); + + class SHA1 { + public: + SHA1Context context; + bool isFinalized = false; + unsigned char output[SHA1_DIGEST_SIZE] = {0}; + + SHA1 (); + SHA1 (const String&); + SHA1 (const SharedPointer&, size_t); + SHA1 (const unsigned char *, size_t); + + SHA1& update (const unsigned char *, size_t); + SHA1& update (const String&); + bool finalized () const; + Vector finalize (); + String str (); + inline size_t size () const { + return SHA1_DIGEST_SIZE; + } + }; + + const String sha1 (const String&); + const String sha1 (const SharedPointer&, size_t); + const String sha1 (const unsigned char *, size_t); +} +#endif diff --git a/src/runtime/crypto/rand.cc b/src/runtime/crypto/rand.cc new file mode 100644 index 0000000000..5ec430a024 --- /dev/null +++ b/src/runtime/crypto/rand.cc @@ -0,0 +1,23 @@ +#include "../crypto.hh" + +#define IMAX_BITS(m) ((m)/((m) % 255+1) / 255 % 255 * 8 + 7-86 / ((m) % 255+12)) +#define RAND_MAX_WIDTH IMAX_BITS(RAND_MAX) + +namespace ssc::runtime::crypto { + uint64_t rand64 () { + static const auto width = RAND_MAX_WIDTH; + static bool init = false; + + if (!init) { + init = true; + srand(time(0)); + } + + uint64_t r = 0; + for (int i = 0; i < 64; i += width) { + r <<= width; + r ^= (unsigned) rand(); + } + return r; + } +} diff --git a/src/runtime/crypto/sha1.cc b/src/runtime/crypto/sha1.cc new file mode 100644 index 0000000000..958dded7d9 --- /dev/null +++ b/src/runtime/crypto/sha1.cc @@ -0,0 +1,218 @@ +#include + +#include "../crypto.hh" +#include "../string.hh" +#include "../bytes.hh" + +using namespace ssc::runtime::string; +using namespace ssc::runtime::bytes; + +#define SHA1_ROTL(x,n) (((x) << (n)) | ((x) >> (32-(n)))) +#define SHA1_CH(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) +#define SHA1_PARITY(x,y,z) ((x) ^ (y) ^ (z)) +#define SHA1_MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +namespace ssc::runtime::crypto { + static void sha1_transform ( + uint32_t state[5], + const unsigned char buffer[64] + ) { + uint32_t a, b, c, d, e; + uint32_t w[80]; + size_t t; + + for (t = 0; t < 16; t++) { + w[t] = (uint32_t)buffer[t * 4] << 24; + w[t] |= (uint32_t)buffer[t * 4 + 1] << 16; + w[t] |= (uint32_t)buffer[t * 4 + 2] << 8; + w[t] |= (uint32_t)buffer[t * 4 + 3]; + } + + for (t = 16; t < 80; t++) { + w[t] = SHA1_ROTL((w[t-3] ^ w[t-8] ^ w[t-14] ^ w[t-16]), 1); + } + + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + + // round 1 + for (t = 0; t < 20; t++) { + uint32_t temp = SHA1_ROTL(a,5) + SHA1_CH(b,c,d) + e + w[t] + 0x5A827999; + e = d; d = c; c = SHA1_ROTL(b,30); b = a; a = temp; + } + + // round 2 + for (; t < 40; t++) { + uint32_t temp = SHA1_ROTL(a,5) + SHA1_PARITY(b,c,d) + e + w[t] + 0x6ED9EBA1; + e = d; d = c; c = SHA1_ROTL(b,30); b = a; a = temp; + } + + // round 3 + for (; t < 60; t++) { + uint32_t temp = SHA1_ROTL(a,5) + SHA1_MAJ(b,c,d) + e + w[t] + 0x8F1BBCDC; + e = d; d = c; c = SHA1_ROTL(b,30); b = a; a = temp; + } + + // round 4 + for (; t < 80; t++) { + uint32_t temp = SHA1_ROTL(a,5) + SHA1_PARITY(b,c,d) + e + w[t] + 0xCA62C1D6; + e = d; d = c; c = SHA1_ROTL(b,30); b = a; a = temp; + } + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + } + + void sha1_init (SHA1Context *ctx) { + ctx->bitCount = 0; + ctx->state[0] = 0x67452301UL; + ctx->state[1] = 0xEFCDAB89UL; + ctx->state[2] = 0x98BADCFEUL; + ctx->state[3] = 0x10325476UL; + ctx->state[4] = 0xC3D2E1F0UL; + memset(ctx->buffer, 0, sizeof(ctx->buffer)); + } + + void sha1_update(SHA1Context *ctx, const unsigned char *data, size_t size) { + size_t i = 0; + size_t index = (size_t)((ctx->bitCount >> 3) & 0x3F); + ctx->bitCount += (uint64_t)size << 3; + + size_t space = 64 - index; + + if (size >= space) { + memcpy(&ctx->buffer[index], data, space); + sha1_transform(ctx->state, ctx->buffer); + for (i = space; i + 63 < size; i += 64) { + sha1_transform(ctx->state, &data[i]); + } + index = 0; + } else { + i = 0; + } + memcpy(&ctx->buffer[index], &data[i], size - i); + } + + void sha1_final(SHA1Context *ctx, unsigned char out[SHA1_DIGEST_SIZE]) { + static const unsigned char pad[64] = { 0x80 }; + unsigned char bits[8]; + size_t index = (size_t)((ctx->bitCount >> 3) & 0x3F); + size_t padSize; + uint64_t bitCount_be = (ctx->bitCount); + + /* convert bit count to big-endian */ + bits[0] = (unsigned char)((bitCount_be >> 56) & 0xFF); + bits[1] = (unsigned char)((bitCount_be >> 48) & 0xFF); + bits[2] = (unsigned char)((bitCount_be >> 40) & 0xFF); + bits[3] = (unsigned char)((bitCount_be >> 32) & 0xFF); + bits[4] = (unsigned char)((bitCount_be >> 24) & 0xFF); + bits[5] = (unsigned char)((bitCount_be >> 16) & 0xFF); + bits[6] = (unsigned char)((bitCount_be >> 8) & 0xFF); + bits[7] = (unsigned char)(bitCount_be & 0xFF); + + padSize = (index < 56) ? (56 - index) : (120 - index); + sha1_update(ctx, pad, padSize); + sha1_update(ctx, bits, 8); + + for (int i = 0; i < 5; i++) { + out[i*4] = (unsigned char)((ctx->state[i] >> 24) & 0xFF); + out[i*4 + 1] = (unsigned char)((ctx->state[i] >> 16) & 0xFF); + out[i*4 + 2] = (unsigned char)((ctx->state[i] >> 8) & 0xFF); + out[i*4 + 3] = (unsigned char)(ctx->state[i] & 0xFF); + } + + /* wipe context */ + memset(ctx, 0, sizeof(*ctx)); + } + + void sha1 ( + const unsigned char *data, + size_t size, + unsigned char out[SHA1_DIGEST_SIZE] + ) { + memset(out, 0, SHA1_DIGEST_SIZE); + SHA1Context ctx; + sha1_init(&ctx); + sha1_update(&ctx, data, size); + sha1_final(&ctx, out); + } + + SHA1::SHA1 () { + sha1_init(&this->context); + } + + SHA1::SHA1 (const String& data) : SHA1() { + this->update(data); + } + + SHA1::SHA1 ( + const SharedPointer& data, + size_t size + ) : SHA1() { + this->update( + reinterpret_cast(data.get()), + size + ); + } + + SHA1::SHA1 ( + const unsigned char* data, + size_t size + ) : SHA1() { + this->update(data, size); + } + + SHA1& SHA1::update ( + const unsigned char* data, + size_t size + ) { + sha1_update(&this->context, data, size); + return *this; + } + + SHA1& SHA1::update (const String& string) { + return this->update( + reinterpret_cast(string.data()), + string.size() + ); + } + + bool SHA1::finalized () const { + return this->isFinalized; + } + + Vector SHA1::finalize () { + if (this->isFinalized == false) { + sha1_final(&this->context, this->output); + this->isFinalized = true; + } + + return Vector( + this->output, + this->output + SHA1_DIGEST_SIZE + ); + } + + String SHA1::str () { + return toUpperCase(encodeHexString(this->finalize())); + } + + const String sha1 (const String& input) { + return SHA1(input).str(); + } + + const String sha1 (const SharedPointer& input, size_t size) { + return SHA1(input, size).str(); + } + + const String sha1 (const unsigned char * input, size_t size) { + return SHA1(input, size).str(); + } + +} diff --git a/src/runtime/cwd.hh b/src/runtime/cwd.hh new file mode 100644 index 0000000000..17331fbcac --- /dev/null +++ b/src/runtime/cwd.hh @@ -0,0 +1,11 @@ +#ifndef SOCKET_RUNTIME_cWD_H +#define SOCKET_RUNTIME_cWD_H + +#include "platform.hh" + +namespace ssc::runtime { + void setcwd (const String& value); + const String getcwd_state_value (); + const String getcwd (); +} +#endif diff --git a/src/core/cwd.cc b/src/runtime/cwd/cwd.cc similarity index 72% rename from src/core/cwd.cc rename to src/runtime/cwd/cwd.cc index 341010144b..f6310c597d 100644 --- a/src/core/cwd.cc +++ b/src/runtime/cwd/cwd.cc @@ -1,7 +1,8 @@ -#include "core.hh" -#include "resource.hh" +#include "../filesystem.hh" -namespace SSC { +#include "../cwd.hh" + +namespace ssc::runtime { static struct { Mutex mutex; String value = ""; } state; void setcwd (const String& value) { @@ -21,7 +22,7 @@ namespace SSC { return state.value; } - state.value = FileResource::getResourcesPath().string(); + state.value = runtime::filesystem::Resource::getResourcesPath().string(); return state.value; } } diff --git a/src/core/trace.hh b/src/runtime/debug.hh similarity index 67% rename from src/core/trace.hh rename to src/runtime/debug.hh index 21c1739951..ecdfc50ca8 100644 --- a/src/core/trace.hh +++ b/src/runtime/debug.hh @@ -1,10 +1,75 @@ -#ifndef SOCKET_RUNTIME_CORE_TRACE_H -#define SOCKET_RUNTIME_CORE_TRACE_H +#ifndef SOCKET_RUNTIME_RUNTIME_DEBUG_H +#define SOCKET_RUNTIME_RUNTIME_DEBUG_H -#include "../platform/platform.hh" +#include "platform.hh" +#include "config.hh" #include "json.hh" -namespace SSC { +#if SOCKET_RUNTIME_PLATFORM_APPLE +// Apple +#ifndef debug +// define `socket_runtime_os_log_debug` (macos/ios) +#if defined(SSC_CLI) +# define socket_runtime_os_log_debug(...) +#else +static os_log_t SOCKET_RUNTIME_OS_LOG_DEBUG = nullptr; +// wrap `os_log*` functions for global debugger +#define socket_runtime_os_log_debug(format, fmt, ...) ({ \ + if (!SOCKET_RUNTIME_OS_LOG_DEBUG) { \ + static auto userConfig = ssc::getUserConfig(); \ + static auto bundleIdentifier = userConfig["meta_bundle_identifier"]; \ + SOCKET_RUNTIME_OS_LOG_DEBUG = os_log_create( \ + bundleIdentifier.c_str(), \ + "socket.runtime.debug" \ + ); \ + } \ + \ + auto string = [NSString stringWithFormat: @fmt, ##__VA_ARGS__]; \ + os_log_error( \ + SOCKET_RUNTIME_OS_LOG_DEBUG, \ + "%{public}s", \ + string.UTF8String \ + ); \ +}) +#endif + +// define `debug(...)` macro +#define debug(format, ...) ({ \ + NSLog(@format, ##__VA_ARGS__); \ + socket_runtime_os_log_debug("%{public}@", format, ##__VA_ARGS__); \ +}) +#endif // `debug` +#endif // `__APPLE__` + +// Linux +#if SOCKET_RUNTIME_PLATFORM_LINUX +# ifndef debug +# define debug(format, ...) fprintf(stderr, format "\n", ##__VA_ARGS__) +# endif // `debug` +#endif // `__linux__` + +// Android (Linux) +#if SOCKET_RUNTIME_PLATFORM_ANDROID +# ifndef debug +# define debug(format, ...) \ + __android_log_print( \ + ANDROID_LOG_DEBUG, \ + "Console", \ + format, \ + ##__VA_ARGS__ \ + ); +# endif // `debug` +#endif // `__ANDROID__` + +// Windows +#if SOCKET_RUNTIME_PLATFORM_WINDOWS && defined(DEBUG) +# define _WIN32_DEBUG 1 +#endif // `_WIN32 && DEBUG` +#ifndef debug +# define debug(format, ...) fprintf(stderr, format "\n", ##__VA_ARGS__) +#endif // `debug` + +namespace ssc::runtime::debug { /** * The `Tracer` class manages multiple `Tracer::Span` instances, allowing * spans to be created and tracked across multiple threads. @@ -39,7 +104,7 @@ namespace SSC { /** * A mapping type of `Span::ID` to `Vector` index for fast span access */ - using SharedSpanColletionIndex = std::map; + using SharedSpanColletionIndex = Map; /** * A `Tracer` ID type @@ -249,5 +314,4 @@ namespace SSC { const bool clear () noexcept; }; } - #endif diff --git a/src/core/console.kt b/src/runtime/debug/console.kt similarity index 91% rename from src/core/console.kt rename to src/runtime/debug/console.kt index be43837e82..c3ef13837e 100644 --- a/src/core/console.kt +++ b/src/runtime/debug/console.kt @@ -1,4 +1,4 @@ -package socket.runtime.core +package socket.runtime.debug object console { val TAG = "Console" diff --git a/src/core/trace.cc b/src/runtime/debug/trace.cc similarity index 96% rename from src/core/trace.cc rename to src/runtime/debug/trace.cc index 390d65eff1..80c0445d3d 100644 --- a/src/core/trace.cc +++ b/src/runtime/debug/trace.cc @@ -1,6 +1,6 @@ -#include "trace.hh" +#include "../debug.hh" -namespace SSC { +namespace ssc::runtime::debug { Tracer::Tracer (const String& name) : name(name), spans(std::make_shared()) @@ -12,7 +12,7 @@ namespace SSC { {} Tracer::Tracer (Tracer&& tracer) noexcept - : name(tracer.name), + : name(std::move(tracer.name)), spans(std::move(tracer.spans)) {} @@ -26,8 +26,10 @@ namespace SSC { Tracer& Tracer::operator= (Tracer&& tracer) noexcept { if (this != &tracer) { - this->name = tracer.name; + this->name = std::move(tracer.name); this->spans = std::move(tracer.spans); + tracer.name = ""; + tracer.spans = nullptr; } return *this; } diff --git a/src/core/env.hh b/src/runtime/env.hh similarity index 70% rename from src/core/env.hh rename to src/runtime/env.hh index 66471a4547..fd654bc671 100644 --- a/src/core/env.hh +++ b/src/runtime/env.hh @@ -1,9 +1,9 @@ -#ifndef SOCKET_RUNTIME_CORE_ENV_H -#define SOCKET_RUNTIME_CORE_ENV_H +#ifndef SOCKET_RUNTIME_ENV_H +#define SOCKET_RUNTIME_ENV_H -#include "../platform/types.hh" +#include "platform.hh" -namespace SSC::Env { +namespace ssc::runtime::env { bool has (const char* name); bool has (const String& name); @@ -14,5 +14,4 @@ namespace SSC::Env { void set (const String& name, const String& value); void set (const char* name); } - #endif diff --git a/src/core/env.cc b/src/runtime/env/env.cc similarity index 75% rename from src/core/env.cc rename to src/runtime/env/env.cc index 65bda894b2..0f44146f1f 100644 --- a/src/core/env.cc +++ b/src/runtime/env/env.cc @@ -1,13 +1,18 @@ #include -#include "config.hh" -#include "env.hh" +#include "../config.hh" +#include "../string.hh" +#include "../env.hh" -namespace SSC::Env { +using namespace ssc::runtime::config; +using namespace ssc::runtime::string; + +namespace ssc::runtime::env { bool has (const char* name) { - static auto userConfig = getUserConfig(); + static const auto userConfig = getUserConfig(); + const auto key = String("env_") + name; - if (userConfig[String("env_") + name].size() > 0) { + if (userConfig.contains(key) && !userConfig.at(key).empty()) { return true; } @@ -27,7 +32,7 @@ namespace SSC::Env { return false; } #else - auto value = getenv(name); + const auto value = getenv(name); if (value == nullptr || value[0] == '\0') { return false; @@ -42,10 +47,11 @@ namespace SSC::Env { } String get (const char* name) { - static auto userConfig = getUserConfig(); + static const auto userConfig = getUserConfig(); + const auto key = String("env_") + name; - if (userConfig[String("env_") + name].size() > 0) { - return userConfig[String("env_") + name]; + if (userConfig.contains(key) && !userConfig.at(key).empty()) { + return userConfig.at(key); } #if SOCKET_RUNTIME_PLATFORM_WINDOWS diff --git a/src/core/resource.hh b/src/runtime/filesystem.hh similarity index 62% rename from src/core/resource.hh rename to src/runtime/filesystem.hh index 525855a287..97d730001c 100644 --- a/src/core/resource.hh +++ b/src/runtime/filesystem.hh @@ -1,28 +1,12 @@ -#ifndef SOCKET_RUNTIME_CORE_RESOURCE_H -#define SOCKET_RUNTIME_CORE_RESOURCE_H +#ifndef SOCKET_RUNTIME_FILESYSTEM_H +#define SOCKET_RUNTIME_FILESYSTEM_H -#include "../platform/platform.hh" +#include "platform.hh" +#include "resource.hh" +#include "core.hh" -#include "trace.hh" -#include "url.hh" - -namespace SSC { - // forward - class Core; - - class Resource { - public: - Atomic accessing = false; - String name; - String type; - Tracer tracer; - Resource (const String& type, const String& name); - bool hasAccess () const noexcept; - virtual bool startAccessing () = 0; - virtual bool stopAccessing () = 0; - }; - - class FileResource : public Resource { +namespace ssc::runtime::filesystem { + class Resource : public ssc::runtime::Resource { public: struct Cache { SharedPointer bytes = nullptr; @@ -114,14 +98,16 @@ namespace SSC { struct Options { bool cache; - Core* core; + #if SOCKET_RUNTIME_PLATFORM_ANDROID + android::ContentResolver* contentResolver; + #endif }; Cache cache; Options options; SharedPointer bytes = nullptr; - static std::map> mimeTypes; + static Map> mimeTypes; static Path getResourcesPath (); static Path getResourcePath (const Path& resourcePath); static Path getResourcePath (const String& resourcePath); @@ -131,15 +117,15 @@ namespace SSC { static bool isDirectory (const Path& resourcePath); static bool isMountedPath (const Path& path); static const WellKnownPaths& getWellKnownPaths (); - static const Map getMountedPaths (); + static const Map getMountedPaths (); #if SOCKET_RUNTIME_PLATFORM_WINDOWS static const Path getMicrosoftEdgeRuntimePath (); #endif #if SOCKET_RUNTIME_PLATFORM_ANDROID - static void setSharedAndroidAssetManager (Android::AssetManager*); - static Android::AssetManager* getSharedAndroidAssetManager (); + static void setSharedAndroidAssetManager (android::AssetManager*); + static android::AssetManager* getSharedAndroidAssetManager (); static void setExternalAndroidStorageDirectory (const Path&); static Path getExternalAndroidStorageDirectory (); @@ -158,21 +144,21 @@ namespace SSC { NSURL* nsURL = nullptr; #endif - FileResource ( + Resource ( const Path& resourcePath, const Options& options = {0} ); - FileResource ( + Resource ( const String& resourcePath, const Options& options = {0} ); - ~FileResource (); - FileResource (const FileResource&); - FileResource (FileResource&&); - FileResource& operator= (const FileResource&); - FileResource& operator= (FileResource&&); + ~Resource (); + Resource (const Resource&); + Resource (Resource&&); + Resource& operator= (const Resource&); + Resource& operator= (Resource&&); bool startAccessing (); bool stopAccessing (); @@ -191,5 +177,88 @@ namespace SSC { bool isAndroidContent () const noexcept; #endif }; + + class Watcher { + public: + // uv types + using EventHandle = uv_fs_event_t; + using PollHandle = uv_fs_poll_t; + using Loop = uv_loop_t; + using Async = uv_async_t; + using Stat = uv_stat_t; + + struct Handle { + EventHandle event; + PollHandle poll; + }; + + // std types + using Clock = std::chrono::high_resolution_clock; + using HandleMap = Map; + using Path = std::filesystem::path; + using Thread = std::thread; + using TimePoint = std::chrono::time_point; + + // event types + enum class Event { + UNKNOWN, + RENAME, + CHANGE + }; + + // callback context + struct Context { + String name; // filename or directory name + Watcher* watcher; + bool isDirectory = false; + TimePoint lastUpdated; + }; + + struct Options { + int debounce = 250; // in milliseconds + bool recursive = true; + }; + + using EventCallback = Function, // events + const Context // event context + )>; + + using ContextMap = Map; + + // state + EventCallback callback = nullptr; + Vector paths; + Vector watchedPaths; + ContextMap contexts; + HandleMap handles; + Options options; + AtomicBool ownsLoop = false; + AtomicBool isRunning = false; + loop::Loop* loop = nullptr; + + static void handleEventCallback ( + EventHandle* handle, + const char* filename, + int events, + int status + ); + + static void handlePollCallback ( + PollHandle* handle, + int status, + const Stat* previousStat, + const Stat* currentStat + ); + + Watcher (const String& path); + Watcher (const Vector& paths); + ~Watcher (); + bool start (EventCallback callback); + bool stop (); + }; + + const Map& constants (); } #endif diff --git a/src/runtime/filesystem/constants.cc b/src/runtime/filesystem/constants.cc new file mode 100644 index 0000000000..d0943b8d67 --- /dev/null +++ b/src/runtime/filesystem/constants.cc @@ -0,0 +1,174 @@ +#include "../filesystem.hh" + +namespace ssc::runtime::filesystem { + #define CONSTANT(c) { #c, (c) }, + static const Map CONSTANTS = { + #if defined(UV_DIRENT_UNKNOWN) + CONSTANT(UV_DIRENT_UNKNOWN) + #endif + #if defined(UV_DIRENT_FILE) + CONSTANT(UV_DIRENT_FILE) + #endif + #if defined(UV_DIRENT_DIR) + CONSTANT(UV_DIRENT_DIR) + #endif + #if defined(UV_DIRENT_LINK) + CONSTANT(UV_DIRENT_LINK) + #endif + #if defined(UV_DIRENT_FIFO) + CONSTANT(UV_DIRENT_FIFO) + #endif + #if defined(UV_DIRENT_SOCKET) + CONSTANT(UV_DIRENT_SOCKET) + #endif + #if defined(UV_DIRENT_CHAR) + CONSTANT(UV_DIRENT_CHAR) + #endif + #if defined(UV_DIRENT_BLOCK) + CONSTANT(UV_DIRENT_BLOCK) + #endif + #if defined(UV_FS_O_FILEMAP) + CONSTANT(UV_FS_O_FILEMAP) + #endif + #if defined(O_RDONLY) + CONSTANT(O_RDONLY) + #endif + #if defined(O_WRONLY) + CONSTANT(O_WRONLY) + #endif + #if defined(O_RDWR) + CONSTANT(O_RDWR) + #endif + #if defined(O_APPEND) + CONSTANT(O_APPEND) + #endif + #if defined(O_ASYNC) + CONSTANT(O_ASYNC) + #endif + #if defined(O_CLOEXEC) + CONSTANT(O_CLOEXEC) + #endif + #if defined(O_CREAT) + CONSTANT(O_CREAT) + #endif + #if defined(O_DIRECT) + CONSTANT(O_DIRECT) + #endif + #if defined(O_DIRECTORY) + CONSTANT(O_DIRECTORY) + #endif + #if defined(O_DSYNC) + CONSTANT(O_DSYNC) + #endif + #if defined(O_EXCL) + CONSTANT(O_EXCL) + #endif + #if defined(O_LARGEFILE) + CONSTANT(O_LARGEFILE) + #endif + #if defined(O_NOATIME) + CONSTANT(O_NOATIME) + #endif + #if defined(O_NOCTTY) + CONSTANT(O_NOCTTY) + #endif + #if defined(O_NOFOLLOW) + CONSTANT(O_NOFOLLOW) + #endif + #if defined(O_NONBLOCK) + CONSTANT(O_NONBLOCK) + #endif + #if defined(O_NDELAY) + CONSTANT(O_NDELAY) + #endif + #if defined(O_PATH) + CONSTANT(O_PATH) + #endif + #if defined(O_SYNC) + CONSTANT(O_SYNC) + #endif + #if defined(O_TMPFILE) + CONSTANT(O_TMPFILE) + #endif + #if defined(O_TRUNC) + CONSTANT(O_TRUNC) + #endif + #if defined(S_IFMT) + CONSTANT(S_IFMT) + #endif + #if defined(S_IFREG) + CONSTANT(S_IFREG) + #endif + #if defined(S_IFDIR) + CONSTANT(S_IFDIR) + #endif + #if defined(S_IFCHR) + CONSTANT(S_IFCHR) + #endif + #if defined(S_IFBLK) + CONSTANT(S_IFBLK) + #endif + #if defined(S_IFIFO) + CONSTANT(S_IFIFO) + #endif + #if defined(S_IFLNK) + CONSTANT(S_IFLNK) + #endif + #if defined(S_IFSOCK) + CONSTANT(S_IFSOCK) + #endif + #if defined(S_IRWXU) + CONSTANT(S_IRWXU) + #endif + #if defined(S_IRUSR) + CONSTANT(S_IRUSR) + #endif + #if defined(S_IWUSR) + CONSTANT(S_IWUSR) + #endif + #if defined(S_IXUSR) + CONSTANT(S_IXUSR) + #endif + #if defined(S_IRWXG) + CONSTANT(S_IRWXG) + #endif + #if defined(S_IRGRP) + CONSTANT(S_IRGRP) + #endif + #if defined(S_IWGRP) + CONSTANT(S_IWGRP) + #endif + #if defined(S_IXGRP) + CONSTANT(S_IXGRP) + #endif + #if defined(S_IRWXO) + CONSTANT(S_IRWXO) + #endif + #if defined(S_IROTH) + CONSTANT(S_IROTH) + #endif + #if defined(S_IWOTH) + CONSTANT(S_IWOTH) + #endif + #if defined(S_IXOTH) + CONSTANT(S_IXOTH) + #endif + #if defined(F_OK) + CONSTANT(F_OK) + #endif + #if defined(R_OK) + CONSTANT(R_OK) + #endif + #if defined(W_OK) + CONSTANT(W_OK) + #endif + #if defined(X_OK) + CONSTANT(X_OK) + #endif + }; + #undef CONSTANT + + const Map& constants () { + return CONSTANTS; + } +} diff --git a/src/core/resource.cc b/src/runtime/filesystem/resource.cc similarity index 82% rename from src/core/resource.cc rename to src/runtime/filesystem/resource.cc index 96272f7d22..658821a16c 100644 --- a/src/core/resource.cc +++ b/src/runtime/filesystem/resource.cc @@ -1,18 +1,20 @@ -#include "config.hh" -#include "core.hh" -#include "debug.hh" -#include "resource.hh" - -#include "../platform/platform.hh" +#include "../env.hh" +#include "../cwd.hh" +#include "../config.hh" +#include "../string.hh" +#include "../filesystem.hh" #if SOCKET_RUNTIME_PLATFORM_ANDROID #include #endif -namespace SSC { - static std::map caches; +using namespace ssc::runtime::string; +using ssc::runtime::config::getUserConfig; + +namespace ssc::runtime::filesystem { + static Map caches; static Mutex mutex; - static FileResource::WellKnownPaths defaultWellKnownPaths; + static Resource::WellKnownPaths defaultWellKnownPaths; #if SOCKET_RUNTIME_PLATFORM_WINDOWS static const String escapeWindowsPath (const Path& path) { @@ -35,7 +37,7 @@ namespace SSC { static Path externalAndroidCacheDirectory; #endif - std::map> FileResource::mimeTypes = { + Map> Resource::mimeTypes = { {"application/font-woff", { ".woff" }}, {"application/font-woff2", { ".woff2" }}, {"application/x-font-opentype", { ".otf" }}, @@ -60,19 +62,9 @@ namespace SSC { {"video/ogg", { ".ogv" }} }; - Resource::Resource (const String& type, const String& name) - : name(name), - type(type), - tracer(name) - {} - - bool Resource::hasAccess () const noexcept { - return this->accessing; - } - #if SOCKET_RUNTIME_PLATFORM_ANDROID static Path getRelativeAndroidAssetManagerPath (const Path& resourcePath) { - auto resourcesPath = FileResource::getResourcesPath(); + auto resourcesPath = Resource::getResourcesPath(); auto assetPath = replace(resourcePath.string(), resourcesPath.string(), ""); if (assetPath.starts_with("/")) { @@ -86,45 +78,45 @@ namespace SSC { #endif #if SOCKET_RUNTIME_PLATFORM_ANDROID - void FileResource::setSharedAndroidAssetManager (Android::AssetManager* assetManager) { + void Resource::setSharedAndroidAssetManager (Android::AssetManager* assetManager) { Lock lock(mutex); sharedAndroidAssetManager = assetManager; } - Android::AssetManager* FileResource::getSharedAndroidAssetManager () { + Android::AssetManager* Resource::getSharedAndroidAssetManager () { return sharedAndroidAssetManager; } - void FileResource::setExternalAndroidStorageDirectory (const Path& directory) { + void Resource::setExternalAndroidStorageDirectory (const Path& directory) { externalAndroidStorageDirectory = directory; } - Path FileResource::getExternalAndroidStorageDirectory () { + Path Resource::getExternalAndroidStorageDirectory () { return externalAndroidStorageDirectory; } - void FileResource::setExternalAndroidFilesDirectory (const Path& directory) { + void Resource::setExternalAndroidFilesDirectory (const Path& directory) { externalAndroidFilesDirectory = directory; } - Path FileResource::getExternalAndroidFilesDirectory () { + Path Resource::getExternalAndroidFilesDirectory () { return externalAndroidFilesDirectory; } - void FileResource::setExternalAndroidCacheDirectory (const Path& directory) { + void Resource::setExternalAndroidCacheDirectory (const Path& directory) { externalAndroidCacheDirectory = directory; } - Path FileResource::getExternalAndroidCacheDirectory () { + Path Resource::getExternalAndroidCacheDirectory () { return externalAndroidCacheDirectory; } #endif - bool FileResource::isFile (const String& resourcePath) { - return FileResource::isFile(Path(resourcePath)); + bool Resource::isFile (const String& resourcePath) { + return Resource::isFile(Path(resourcePath)); } - bool FileResource::isFile (const Path& resourcePath) { + bool Resource::isFile (const Path& resourcePath) { #if SOCKET_RUNTIME_PLATFORM_ANDROID if (sharedAndroidAssetManager) { const auto assetPath = getRelativeAndroidAssetManagerPath(resourcePath); @@ -153,11 +145,11 @@ namespace SSC { return fs::is_regular_file(resourcePath); } - bool FileResource::isDirectory (const String& resourcePath) { - return FileResource::isDirectory(Path(resourcePath)); + bool Resource::isDirectory (const String& resourcePath) { + return Resource::isDirectory(Path(resourcePath)); } - bool FileResource::isDirectory (const Path& resourcePath) { + bool Resource::isDirectory (const Path& resourcePath) { #if SOCKET_RUNTIME_PLATFORM_ANDROID if (sharedAndroidAssetManager) { const auto assetPath = getRelativeAndroidAssetManagerPath(resourcePath); @@ -185,9 +177,9 @@ namespace SSC { return fs::is_directory(resourcePath); } - bool FileResource::isMountedPath (const Path& path) { + bool Resource::isMountedPath (const Path& path) { static auto userConfig = getUserConfig(); - static auto mounts = FileResource::getMountedPaths(); + static auto mounts = Resource::getMountedPaths(); for (const auto& entry : mounts) { if (path.string().starts_with(entry.first)) { return true; @@ -196,9 +188,9 @@ namespace SSC { return false; } - const Map FileResource::getMountedPaths () { - static auto userConfig = getUserConfig(); - static Map mounts = {}; + const Map Resource::getMountedPaths () { + static const auto userConfig = getUserConfig(); + static Map mounts = {}; // global if (mounts.size() > 0) { return mounts; @@ -206,7 +198,7 @@ namespace SSC { // determine HOME #if SOCKET_RUNTIME_PLATFORM_WINDOWS - static const auto HOME = Env::get("HOMEPATH", Env::get("USERPROFILE", Env::get("HOME"))); + static const auto HOME = runtime::env::get("HOMEPATH", runtime::env::get("USERPROFILE", runtime::env::get("HOME"))); #elif SOCKET_RUNTIME_PLATFORM_IOS static const auto HOME = String(NSHomeDirectory().UTF8String); #else @@ -214,10 +206,10 @@ namespace SSC { static const auto pwuid = getpwuid(uid); static const auto HOME = pwuid != nullptr ? String(pwuid->pw_dir) - : Env::get("HOME", getcwd()); + : runtime::env::get("HOME", getcwd()); #endif - static const Map mappings = { + static const Map mappings = { {"\\$HOST_HOME", HOME}, {"~", HOME}, @@ -248,7 +240,7 @@ namespace SSC { } }; - static const auto wellKnownPaths = FileResource::getWellKnownPaths(); + static const auto wellKnownPaths = Resource::getWellKnownPaths(); for (const auto& entry : userConfig) { if (entry.first.starts_with("webview_navigator_mounts_")) { @@ -280,7 +272,7 @@ namespace SSC { return mounts; } - Path FileResource::getResourcesPath () { + Path Resource::getResourcesPath () { static String value; if (value.size() > 0) { @@ -326,12 +318,12 @@ namespace SSC { return Path(value); } - Path FileResource::getResourcePath (const Path& resourcePath) { - return FileResource::getResourcePath(resourcePath.string()); + Path Resource::getResourcePath (const Path& resourcePath) { + return Resource::getResourcePath(resourcePath.string()); } - Path FileResource::getResourcePath (const String& resourcePath) { - const auto resourcesPath = FileResource::getResourcesPath(); + Path Resource::getResourcePath (const String& resourcePath) { + const auto resourcesPath = Resource::getResourcesPath(); #if SOCKET_RUNTIME_PLATFORM_WINDOWS if (resourcePath.starts_with("\\")) { return Path(resourcesPath.string() + resourcePath); @@ -347,16 +339,16 @@ namespace SSC { #endif } - void FileResource::WellKnownPaths::setDefaults (const WellKnownPaths& paths) { + void Resource::WellKnownPaths::setDefaults (const WellKnownPaths& paths) { defaultWellKnownPaths = paths; } - const FileResource::WellKnownPaths& FileResource::getWellKnownPaths () { + const Resource::WellKnownPaths& Resource::getWellKnownPaths () { static const auto paths = WellKnownPaths {}; return paths; } - FileResource::WellKnownPaths::WellKnownPaths () { + Resource::WellKnownPaths::WellKnownPaths () { static auto userConfig = getUserConfig(); static auto bundleIdentifier = userConfig["meta_bundle_identifier"]; @@ -374,14 +366,14 @@ namespace SSC { this->log = defaultWellKnownPaths.log; this->tmp = defaultWellKnownPaths.tmp; - this->resources = FileResource::getResourcesPath(); + this->resources = Resource::getResourcesPath(); this->tmp = fs::temp_directory_path(); #if SOCKET_RUNTIME_PLATFORM_APPLE static const auto uid = getuid(); static const auto pwuid = getpwuid(uid); static const auto HOME = pwuid != nullptr ? String(pwuid->pw_dir) - : Env::get("HOME", getcwd()); + : runtime::env::get("HOME", getcwd()); static const auto fileManager = NSFileManager.defaultManager; @@ -416,18 +408,18 @@ namespace SSC { static const auto pwuid = getpwuid(uid); static const auto HOME = pwuid != nullptr ? String(pwuid->pw_dir) - : Env::get("HOME", getcwd()); + : runtime::env::get("HOME", getcwd()); - static const auto XDG_DOCUMENTS_DIR = Env::get("XDG_DOCUMENTS_DIR"); - static const auto XDG_DOWNLOAD_DIR = Env::get("XDG_DOWNLOAD_DIR"); - static const auto XDG_PICTURES_DIR = Env::get("XDG_PICTURES_DIR"); - static const auto XDG_DESKTOP_DIR = Env::get("XDG_DESKTOP_DIR"); - static const auto XDG_VIDEOS_DIR = Env::get("XDG_VIDEOS_DIR"); - static const auto XDG_MUSIC_DIR = Env::get("XDG_MUSIC_DIR"); - static const auto XDG_PUBLICSHARE_DIR = Env::get("XDG_PUBLICSHARE_DIR"); + static const auto XDG_DOCUMENTS_DIR = runtime::env::get("XDG_DOCUMENTS_DIR"); + static const auto XDG_DOWNLOAD_DIR = runtime::env::get("XDG_DOWNLOAD_DIR"); + static const auto XDG_PICTURES_DIR = runtime::env::get("XDG_PICTURES_DIR"); + static const auto XDG_DESKTOP_DIR = runtime::env::get("XDG_DESKTOP_DIR"); + static const auto XDG_VIDEOS_DIR = runtime::env::get("XDG_VIDEOS_DIR"); + static const auto XDG_MUSIC_DIR = runtime::env::get("XDG_MUSIC_DIR"); + static const auto XDG_PUBLICSHARE_DIR = runtime::env::get("XDG_PUBLICSHARE_DIR"); - static const auto XDG_CONFIG_HOME = Env::get("XDG_CONFIG_HOME", HOME + "/.config"); - static const auto XDG_DATA_HOME = Env::get("XDG_DATA_HOME", HOME + "/.local/share"); + static const auto XDG_CONFIG_HOME = runtime::env::get("XDG_CONFIG_HOME", HOME + "/.config"); + static const auto XDG_DATA_HOME = runtime::env::get("XDG_DATA_HOME", HOME + "/.local/share"); if (XDG_DOCUMENTS_DIR.size() > 0) { this->documents = Path(XDG_DOCUMENTS_DIR); @@ -478,27 +470,27 @@ namespace SSC { this->data = Path(XDG_DATA_HOME) / bundleIdentifier; this->log = this->config; #elif SOCKET_RUNTIME_PLATFORM_WINDOWS - static const auto HOME = Env::get("HOMEPATH", Env::get("HOME")); - static const auto USERPROFILE = Env::get("USERPROFILE", HOME); + static const auto HOME = runtime::env::get("HOMEPATH", runtime::env::get("HOME")); + static const auto USERPROFILE = runtime::env::get("USERPROFILE", HOME); this->downloads = escapeWindowsPath(Path(USERPROFILE) / "Downloads"); this->documents = escapeWindowsPath(Path(USERPROFILE) / "Documents"); this->pictures = escapeWindowsPath(Path(USERPROFILE) / "Pictures"); this->desktop = escapeWindowsPath(Path(USERPROFILE) / "Desktop"); this->videos = escapeWindowsPath(Path(USERPROFILE) / "Videos"); this->music = escapeWindowsPath(Path(USERPROFILE) / "Music"); - this->config = escapeWindowsPath(Path(Env::get("APPDATA")) / bundleIdentifier); + this->config = escapeWindowsPath(Path(runtime::env::get("APPDATA")) / bundleIdentifier); this->home = escapeWindowsPath(Path(USERPROFILE)); - this->data = escapeWindowsPath(Path(Env::get("APPDATA")) / bundleIdentifier); + this->data = escapeWindowsPath(Path(runtime::env::get("APPDATA")) / bundleIdentifier); this->log = this->config; #elif SOCKET_RUNTIME_PLATFORM_ANDROID - const auto storage = FileResource::getExternalAndroidStorageDirectory(); - const auto cache = FileResource::getExternalAndroidCacheDirectory(); + const auto storage = Resource::getExternalAndroidStorageDirectory(); + const auto cache = Resource::getExternalAndroidCacheDirectory(); this->resources = "socket://" + bundleIdentifier; this->tmp = !cache.empty() ? cache : storage / "tmp"; #endif } - JSON::Object FileResource::WellKnownPaths::json () const { + JSON::Object Resource::WellKnownPaths::json () const { return JSON::Object::Entries { {"resources", this->resources}, {"downloads", this->downloads}, @@ -516,7 +508,7 @@ namespace SSC { }; } - const Vector FileResource::WellKnownPaths::entries () const { + const Vector Resource::WellKnownPaths::entries () const { auto entries = Vector(); entries.push_back(this->resources); entries.push_back(this->downloads); @@ -535,9 +527,10 @@ namespace SSC { } #if SOCKET_RUNTIME_PLATFORM_WINDOWS - const Path FileResource::getMicrosoftEdgeRuntimePath () { + const Path Resource::getMicrosoftEdgeRuntimePath () { // this is something like "C:\\Users\\jwerle\\AppData\\Local\\Microsoft\\Edge SxS\\Application\\123.0.2386.0" - static const auto EDGE_RUNTIME_DIRECTORY = trim(Env::get("SOCKET_EDGE_RUNTIME_DIRECTORY")); + static const auto EDGE_RUNTIME_DIRECTORY = trim(runtime::env::get("SOCKET_EDGE_RUNTIME_DIRECTORY")); + return Path( EDGE_RUNTIME_DIRECTORY.size() > 0 && fs::exists(EDGE_RUNTIME_DIRECTORY) @@ -547,16 +540,16 @@ namespace SSC { } #endif - FileResource::FileResource ( + Resource::Resource ( const Path& resourcePath, const Options& options ) : - Resource("FileResource", resourcePath.string()) + runtime::Resource("Resource", resourcePath.string()) { this->url = URL(resourcePath.string()); if (url.scheme == "socket") { - const auto resourcesPath = FileResource::getResourcesPath(); + const auto resourcesPath = Resource::getResourcesPath(); this->path = fs::absolute(resourcesPath / url.pathname); #if SOCKET_RUNTIME_PLATFORM_ANDROID this->path = Path(url.pathname); @@ -581,16 +574,16 @@ namespace SSC { #endif } - FileResource::FileResource (const String& resourcePath, const Options& options) - : FileResource(Path(resourcePath), options) + Resource::Resource (const String& resourcePath, const Options& options) + : Resource(Path(resourcePath), options) {} - FileResource::~FileResource () { + Resource::~Resource () { this->stopAccessing(); } - FileResource::FileResource (const FileResource& resource) - : Resource("FileResource", resource.name) + Resource::Resource (const Resource& resource) + : runtime::Resource("Resource", resource.name) { this->url = resource.url; this->path = resource.path; @@ -600,8 +593,8 @@ namespace SSC { this->startAccessing(); } - FileResource::FileResource (FileResource&& resource) - : Resource("FileResource", resource.name) + Resource::Resource (Resource&& resource) + : runtime::Resource("Resource", resource.name) { this->url = resource.url; this->path = resource.path; @@ -619,7 +612,7 @@ namespace SSC { this->startAccessing(); } - FileResource& FileResource::operator= (const FileResource& resource) { + Resource& Resource::operator= (const Resource& resource) { this->url = resource.url; this->path = resource.path; this->bytes = resource.bytes; @@ -632,7 +625,7 @@ namespace SSC { return *this; } - FileResource& FileResource::operator= (FileResource&& resource) { + Resource& Resource::operator= (Resource&& resource) { this->url = resource.url; this->path = resource.path; this->bytes = resource.bytes; @@ -651,8 +644,8 @@ namespace SSC { return *this; } - bool FileResource::startAccessing () { - static const auto resourcesPath = FileResource::getResourcesPath(); + bool Resource::startAccessing () { + static const auto resourcesPath = Resource::getResourcesPath(); if (this->accessing) { return false; @@ -668,7 +661,7 @@ namespace SSC { } #endif - if (FileResource::isMountedPath(this->path)) { + if (Resource::isMountedPath(this->path)) { this->accessing = true; return true; } @@ -686,7 +679,7 @@ namespace SSC { return true; } - bool FileResource::stopAccessing () { + bool Resource::stopAccessing () { if (!this->accessing) { return false; } @@ -699,7 +692,7 @@ namespace SSC { return true; } - bool FileResource::exists () const noexcept { + bool Resource::exists () const noexcept { if (!this->accessing) { return false; } @@ -731,7 +724,7 @@ namespace SSC { #endif } - int FileResource::access (int mode) const noexcept { + int Resource::access (int mode) const noexcept { if (this->accessing) { #if SOCKET_RUNTIME_PLATFORM_ANDROID if (this->isAndroidLocalAsset() || this->isAndroidContent()) { @@ -749,11 +742,11 @@ namespace SSC { return -1; // `EPERM` } - const String FileResource::mimeType () const noexcept { + const String Resource::mimeType () const noexcept { const auto extension = this->path.extension().string(); if (extension.size() > 0) { // try in memory simle mime db - for (const auto& entry : FileResource::mimeTypes) { + for (const auto& entry : Resource::mimeTypes) { const auto& mimeType = entry.first; const auto& extensions = entry.second; if (extensions.contains(extension)) { @@ -841,20 +834,19 @@ namespace SSC { } } - if (this->options.core && this->url.scheme == "content") { - auto core = this->options.core; - return core->platform.contentResolver.getContentMimeType(this->url.str()); + if (this->options.contentResolver && this->url.scheme == "content") { + return this->options.contentResolver.getContentMimeType(this->url.str()); } #endif return ""; } - size_t FileResource::size () const noexcept { + size_t Resource::size () const noexcept { return this->cache.size; } - size_t FileResource::size (bool cached) noexcept { + size_t Resource::size (bool cached) noexcept { if (cached && this->cache.size > 0) { return this->cache.size; } @@ -920,17 +912,16 @@ namespace SSC { } } } else if (this->url.scheme == "content" || this->url.scheme == "android.resource") { - auto core = this->options.core; - if (core != nullptr) { + if (this->options.contentResolver != nullptr) { off_t offset = 0; off_t length = 0; - auto fileDescriptor = core->platform.contentResolver.openFileDescriptor ( + auto fileDescriptor = this->options.contentResolver->openFileDescriptor ( this->url.str(), &offset, &length ); - core->platform.contentResolver.closeFileDescriptor(fileDescriptor); + this->options.contentResolver->closeFileDescriptor(fileDescriptor); return length; } } @@ -943,12 +934,12 @@ namespace SSC { return this->cache.size; } - const char* FileResource::read () const { + const char* Resource::read () const { return this->cache.bytes.get(); } // caller takes ownership of returned pointer - const char* FileResource::read (bool cached) { + const char* Resource::read (bool cached) { if (!this->accessing || !this->exists()) { return nullptr; } @@ -1068,7 +1059,7 @@ namespace SSC { return this->cache.bytes.get(); } - const String FileResource::str (bool cached) { + const String Resource::str (bool cached) { if (!this->accessing || !this->exists()) { return ""; } @@ -1083,15 +1074,15 @@ namespace SSC { return ""; } - FileResource::ReadStream FileResource::stream (const ReadStream::Options& options) { + Resource::ReadStream Resource::stream (const ReadStream::Options& options) { return ReadStream(ReadStream::Options(this->path, options.highWaterMark, this->size())); } - FileResource::ReadStream::ReadStream (const Options& options) + Resource::ReadStream::ReadStream (const Options& options) : options(options) {} - FileResource::ReadStream::~ReadStream () { + Resource::ReadStream::~ReadStream () { #if SOCKET_RUNTIME_PLATFORM_APPLE #elif SOCKET_RUNTIME_PLATFORM_LINUX if (this->file != nullptr) { @@ -1113,7 +1104,7 @@ namespace SSC { #endif } - FileResource::ReadStream::ReadStream ( + Resource::ReadStream::ReadStream ( const ReadStream& stream ) : options(stream.options), offset(stream.offset.load()), @@ -1136,7 +1127,7 @@ namespace SSC { #endif } - FileResource::ReadStream::ReadStream (ReadStream&& stream) + Resource::ReadStream::ReadStream (ReadStream&& stream) : options(stream.options), offset(stream.offset.load()), ended(stream.ended.load()) @@ -1157,7 +1148,7 @@ namespace SSC { #endif } - FileResource::ReadStream& FileResource::ReadStream::operator = ( + Resource::ReadStream& Resource::ReadStream::operator = ( const ReadStream& stream ) { this->options.highWaterMark = stream.options.highWaterMark; @@ -1184,7 +1175,7 @@ namespace SSC { return *this; } - FileResource::ReadStream& FileResource::ReadStream::operator = ( + Resource::ReadStream& Resource::ReadStream::operator = ( ReadStream&& stream ) { this->options.highWaterMark = stream.options.highWaterMark; @@ -1210,7 +1201,7 @@ namespace SSC { return *this; } - const FileResource::ReadStream::Buffer FileResource::ReadStream::read (off_t offset, size_t highWaterMark) { + const Resource::ReadStream::Buffer Resource::ReadStream::read (off_t offset, size_t highWaterMark) { if (offset == -1) { offset = this->offset; } @@ -1284,7 +1275,7 @@ namespace SSC { if (this->error) { buffer.size = 0; debug( - "FileResource::ReadStream: read error: %s", + "Resource::ReadStream: read error: %s", #if SOCKET_RUNTIME_PLATFORM_APPLE error.localizedDescription.UTF8String #elif SOCKET_RUNTIME_PLATFORM_LINUX @@ -1307,7 +1298,7 @@ namespace SSC { return buffer; } - size_t FileResource::ReadStream::remaining (off_t offset) const { + size_t Resource::ReadStream::remaining (off_t offset) const { const auto size = this->options.size; if (offset > -1) { return size - offset; @@ -1316,33 +1307,33 @@ namespace SSC { return size - this->offset; } - FileResource::ReadStream::Buffer::Buffer (size_t size) + Resource::ReadStream::Buffer::Buffer (size_t size) : bytes(std::make_shared(size)), size(size) { memset(this->bytes.get(), 0, size); } - FileResource::ReadStream::Buffer::Buffer (const Options& options) + Resource::ReadStream::Buffer::Buffer (const Options& options) : bytes(std::make_shared(options.highWaterMark)), size(0) { memset(this->bytes.get(), 0, options.highWaterMark); } - FileResource::ReadStream::Buffer::Buffer (const Buffer& buffer) { + Resource::ReadStream::Buffer::Buffer (const Buffer& buffer) { this->size = buffer.size.load(); this->bytes = buffer.bytes; } - FileResource::ReadStream::Buffer::Buffer (Buffer&& buffer) { + Resource::ReadStream::Buffer::Buffer (Buffer&& buffer) { this->size = buffer.size.load(); this->bytes = buffer.bytes; buffer.size = 0; buffer.bytes = nullptr; } - FileResource::ReadStream::Buffer& FileResource::ReadStream::Buffer::operator = ( + Resource::ReadStream::Buffer& Resource::ReadStream::Buffer::operator = ( const Buffer& buffer ) { this->size = buffer.size.load(); @@ -1350,7 +1341,7 @@ namespace SSC { return *this; } - FileResource::ReadStream::Buffer& FileResource::ReadStream::Buffer::operator = ( + Resource::ReadStream::Buffer& Resource::ReadStream::Buffer::operator = ( Buffer&& buffer ) { this->size = buffer.size.load(); @@ -1360,12 +1351,12 @@ namespace SSC { return *this; } - bool FileResource::ReadStream::Buffer::isEmpty () const { + bool Resource::ReadStream::Buffer::isEmpty () const { return this->size == 0 || this->bytes == nullptr; } #if SOCKET_RUNTIME_PLATFORM_ANDROID - bool FileResource::isAndroidLocalAsset () const noexcept { + bool Resource::isAndroidLocalAsset () const noexcept { if (sharedAndroidAssetManager) { const auto assetPath = getRelativeAndroidAssetManagerPath(this->path); const auto asset = AAssetManager_open( @@ -1383,15 +1374,11 @@ namespace SSC { return false; } - bool FileResource::isAndroidContent () const noexcept { - const auto core = this->options.core; - - if (core != nullptr) { - const auto uri = this->path.string(); - if (core->platform.contentResolver.isContentURI(uri)) { - const auto pathname = core->platform.contentResolver.getPathnameFromURI(uri); - return pathname.size() > 0; - } + bool Resource::isAndroidContent () const noexcept { + const auto uri = this->path.string(); + if (this->options.contentResolver->isContentURI(uri)) { + const auto pathname = this->options.contentResolver->getPathnameFromURI(uri); + return pathname.size() > 0; } return false; } diff --git a/src/core/file_system_watcher.cc b/src/runtime/filesystem/watcher.cc similarity index 70% rename from src/core/file_system_watcher.cc rename to src/runtime/filesystem/watcher.cc index d12a68076f..f5b7458e8b 100644 --- a/src/core/file_system_watcher.cc +++ b/src/runtime/filesystem/watcher.cc @@ -1,10 +1,10 @@ -#include "file_system_watcher.hh" -#include "core.hh" +#include "../filesystem.hh" +#include "../debug.hh" -namespace SSC { - static FileSystemWatcher::Path resolveFileNameForContext ( +namespace ssc::runtime::filesystem { + static Watcher::Path resolveFileNameForContext ( const String& filename, - const FileSystemWatcher::Context* context + const Watcher::Context* context ) { static const auto cwd = fs::current_path(); @@ -16,12 +16,12 @@ namespace SSC { } static void handleCallback ( - FileSystemWatcher::Context* context, + Watcher::Context* context, const String filename, const int eventTypes ) { const auto path = resolveFileNameForContext(filename, context); - const auto now = FileSystemWatcher::Clock::now(); + const auto now = Watcher::Clock::now(); if (!std::filesystem::exists(path)) { return; @@ -36,17 +36,17 @@ namespace SSC { } // build events vector - auto events = Vector(); + auto events = Vector(); if (eventTypes == 0) { - events.push_back(FileSystemWatcher::Event::CHANGE); + events.push_back(Watcher::Event::CHANGE); } else { if ((eventTypes & UV_RENAME) == UV_RENAME) { - events.push_back(FileSystemWatcher::Event::RENAME); + events.push_back(Watcher::Event::RENAME); } if ((eventTypes & UV_CHANGE) == UV_CHANGE) { - events.push_back(FileSystemWatcher::Event::CHANGE); + events.push_back(Watcher::Event::CHANGE); } } @@ -56,7 +56,7 @@ namespace SSC { } } - void FileSystemWatcher::handleEventCallback ( + void Watcher::handleEventCallback ( EventHandle* handle, const char* eventTarget, int eventTypes, @@ -67,7 +67,7 @@ namespace SSC { handleCallback(context, filename, eventTypes); } - void FileSystemWatcher::handlePollCallback ( + void Watcher::handlePollCallback ( PollHandle* handle, int status, const Stat* previousStat, @@ -84,7 +84,7 @@ namespace SSC { if (result != 0) { debug( - "FileSystemWatcher: uv_fs_poll_getpath: error: %s\n", + "Watcher: uv_fs_poll_getpath: error: %s\n", uv_strerror(result) ); return; @@ -95,27 +95,27 @@ namespace SSC { handleCallback(context, filename, 0); } - FileSystemWatcher::FileSystemWatcher (const String& path) { + Watcher::Watcher (const String& path) { this->paths.push_back(path); } - FileSystemWatcher::FileSystemWatcher (const Vector& paths) { + Watcher::Watcher (const Vector& paths) { this->paths = paths; } - FileSystemWatcher::~FileSystemWatcher () { + Watcher::~Watcher () { this->paths.clear(); this->watchedPaths.clear(); - if (this->ownsCore && this->core != nullptr) { - delete this->core; + if (this->ownsLoop && this->loop != nullptr) { + delete this->loop; } - this->ownsCore = false; - this->core = nullptr; + this->ownsLoop = false; + this->loop = nullptr; } - bool FileSystemWatcher::start (EventCallback callback) { + bool Watcher::start (EventCallback callback) { if (this->isRunning) { return false; } @@ -124,11 +124,10 @@ namespace SSC { // a loop may be configured for the instance already, perhaps here or // manually by the caller - if (this->core == nullptr) { - Core::Options options; - options.features.useNotifications = false; - this->core = new Core(options); - this->ownsCore = true; + if (this->loop == nullptr) { + loop::Loop::Options options; + this->loop = new loop::Loop(options); + this->ownsLoop = true; } for (const auto &path : this->paths) { @@ -151,8 +150,7 @@ namespace SSC { this->watchedPaths.push_back(path); } - this->core->dispatchEventLoop([this]() mutable { - auto loop = this->core->getEventLoop(); + this->loop->dispatch([this]() mutable { for (const auto &path : this->watchedPaths) { const bool exists = this->handles.contains(path); auto context = &this->contexts[path]; @@ -163,15 +161,15 @@ namespace SSC { if (!exists) { // init context context->isDirectory = std::filesystem::is_directory(path); - context->lastUpdated = FileSystemWatcher::Clock::now(); + context->lastUpdated = Watcher::Clock::now(); context->watcher = this; context->name = std::filesystem::absolute(path).string(); // init uv fs event handle for files AND directories - status = uv_fs_event_init(loop, &handle->event); + status = uv_fs_event_init(this->loop->get(), &handle->event); if (status != 0) { debug( - "FileSystemWatcher: uv_fs_event_init: error: %s\n", + "Watcher: uv_fs_event_init: error: %s\n", uv_strerror(status) ); } @@ -183,11 +181,11 @@ namespace SSC { // use uv_fs_poll for only files if (!context->isDirectory) { // init uv fs poll handle - uv_fs_poll_init(loop, &handle->poll); + uv_fs_poll_init(this->loop->get(), &handle->poll); if (status != 0) { debug( - "FileSystemWatcher: uv_fs_poll_init: error: %s\n", + "Watcher: uv_fs_poll_init: error: %s\n", uv_strerror(status) ); } @@ -197,14 +195,14 @@ namespace SSC { // start (or restart) status = uv_fs_event_start( &handle->event, - FileSystemWatcher::handleEventCallback, + Watcher::handleEventCallback, path.c_str(), this->options.recursive ? UV_FS_EVENT_RECURSIVE : 0 ); if (status != 0) { debug( - "FileSystemWatcher: uv_fs_event_start: error: %s\n", + "Watcher: uv_fs_event_start: error: %s\n", uv_strerror(status) ); } @@ -212,14 +210,14 @@ namespace SSC { if (!context->isDirectory) { status = uv_fs_poll_start( &handle->poll, - FileSystemWatcher::handlePollCallback, + Watcher::handlePollCallback, path.c_str(), 500 ); if (status != 0) { debug( - "FileSystemWatcher: uv_fs_poll_start: error: %s\n", + "Watcher: uv_fs_poll_start: error: %s\n", uv_strerror(status) ); } @@ -231,7 +229,7 @@ namespace SSC { return true; } - bool FileSystemWatcher::stop () { + bool Watcher::stop () { if (!this->isRunning) { return false; } @@ -246,7 +244,7 @@ namespace SSC { if (status != 0) { debug( - "FileSystemWatcher: uv_fs_event_stop: error: %s\n", + "Watcher: uv_fs_event_stop: error: %s\n", uv_strerror(status) ); } @@ -255,7 +253,7 @@ namespace SSC { status = uv_fs_poll_stop(&handle->poll); if (status != 0) { debug( - "FileSystemWatcher: uv_fs_poll_stop: error: %s\n", + "Watcher: uv_fs_poll_stop: error: %s\n", uv_strerror(status) ); } @@ -264,8 +262,8 @@ namespace SSC { } // stop loop if owned by instance - if (this->ownsCore && this->core != nullptr) { - this->core->stopEventLoop(); + if (this->ownsLoop && this->loop != nullptr) { + this->loop->stop(); } return true; diff --git a/src/runtime/http.hh b/src/runtime/http.hh new file mode 100644 index 0000000000..0c2ee3197d --- /dev/null +++ b/src/runtime/http.hh @@ -0,0 +1,138 @@ +#ifndef SOCKET_RUNTIME_RUNTIME_HTTP_H +#define SOCKET_RUNTIME_RUNTIME_HTTP_H + +#include "bytes.hh" +#include "json.hh" +#include "url.hh" + +namespace ssc::runtime::http { + struct Status { + int code = 200; + String text = "OK"; + Status () = default; + Status (int, const String&); + Status (int); + Status (const String&); + String str () const; + JSON::Object json () const; + }; + + String getStatusText (int); + int getStatusCode (const String&); + const Map& getStatusMap (); + + const String toHeaderCase (const String&); + + class Headers { + public: + class Value { + public: + String string; + Value () = default; + Value (const String&); + Value (const char*); + Value (const Value&); + Value (bool); + Value (int); + Value (float); + Value (int64_t); + Value (uint64_t); + Value (double_t); + #if SOCKET_RUNTIME_PLATFORM_APPLE + Value (ssize_t value); + #endif + + bool operator == (const Value&) const; + bool operator != (const Value&) const; + bool operator == (const String&) const; + bool operator != (const String&) const; + const String operator + (const String&) const; + + const String& str () const; + const char * c_str() const; + bool empty () const; + size_t size () const; + + template void set (T value) { + auto v = Value(value); + this->string = v.string; + } + }; + + class Header { + public: + String name; + Value value; + Header () = default; + Header (const Header&); + Header (const String&, const Value&); + bool operator == (const Header&) const; + bool operator != (const Header&) const; + bool operator == (const String&) const; + bool operator != (const String&) const; + const String operator + (const String&) const; + size_t size () const; + bool empty () const; + }; + + using Entries = Vector
; + using Iterator = Entries::const_iterator; + + Entries entries; + Headers () = default; + Headers (const Headers&); + Headers (const String&); + Headers (const Vector>&); + Headers (const Entries&); + size_t size () const; + String str () const; + bool empty () const; + + Headers& set (const String&, const String&) noexcept; + Headers& set (const Header&) noexcept; + bool has (const String&) const noexcept; + const Header get (const String&) const noexcept; + Header& at (const String&); + const Iterator begin () const noexcept; + const Iterator end () const noexcept; + bool erase (const String&) noexcept; + const bool clear () noexcept; + String& operator [] (const String&); + const String operator [] (const String&) const noexcept; + JSON::Object json () const noexcept; + }; + + class Request { + public: + String version = "1.1"; + String method = ""; + URL url; + Headers headers; + bytes::Buffer body; + + Request (const String&); + Request (const unsigned char*, size_t = -1); + String str () const; + bool valid () const; + }; + + class Response { + public: + String version = "1.1"; + Status status; + Headers headers; + bytes::Buffer body; + + Response (const String&); + Response (const Headers&); + Response (const Status&); + Response (const Status&, const Headers&); + + Response& setHeader (const Headers::Header&); + Response& setHeader (const String&, const String&); + + const unsigned char* data () const; + String str () const; + }; +} +#endif diff --git a/src/core/headers.cc b/src/runtime/http/headers.cc similarity index 93% rename from src/core/headers.cc rename to src/runtime/http/headers.cc index 4e062dab43..fc91224ea7 100644 --- a/src/core/headers.cc +++ b/src/runtime/http/headers.cc @@ -1,6 +1,9 @@ -#include "core.hh" +#include "../http.hh" +#include "../string.hh" -namespace SSC { +using namespace ssc::runtime::string; + +namespace ssc::runtime::http { Headers::Header::Header (const Header& header) { this->name = toLowerCase(header.name); this->value = header.value; @@ -52,7 +55,7 @@ namespace SSC { this->entries = headers.entries; } - Headers::Headers (const Vector>& entries) { + Headers::Headers (const Vector>& entries) { for (const auto& entry : entries) { for (const auto& pair : entry) { this->entries.push_back(Header { pair.first, pair.second }); @@ -66,19 +69,21 @@ namespace SSC { } } - void Headers::set (const String& name, const String& value) noexcept { + Headers& Headers::set (const String& name, const String& value) noexcept { set(Header { name, value }); + return *this; } - void Headers::set (const Header& header) noexcept { + Headers& Headers::set (const Header& header) noexcept { for (auto& entry : entries) { if (header.name == entry.name) { entry.value = header.value; - return; + return *this; } } entries.push_back(header); + return *this; } bool Headers::has (const String& name) const noexcept { @@ -143,7 +148,7 @@ namespace SSC { } } - return headers.str(); + return headers.str() + "\r\n"; } const Headers::Iterator Headers::begin () const noexcept { diff --git a/src/runtime/http/request.cc b/src/runtime/http/request.cc new file mode 100644 index 0000000000..ddf19019d4 --- /dev/null +++ b/src/runtime/http/request.cc @@ -0,0 +1,59 @@ +#include "../http.hh" +#include "../string.hh" + +using namespace ssc::runtime::string; + +namespace ssc::runtime::http { + Request::Request (const String& input) { + const auto crlf = input.find("\r\n"); + if (crlf != String::npos) { + auto stream = std::istringstream(input.substr(0, crlf)); + String uri; + stream + >> this->method + >> uri + >> this->version; + + this->url = uri; + this->headers = input.substr(crlf, input.find("\r\n\r\n")); + this->body = input.substr(input.find("\r\n\r\n")); + } + } + + Request::Request (const unsigned char* input, size_t size) { + const auto string = size >= 0 + ? String(reinterpret_cast(input), size) + : String(reinterpret_cast(input)); + + const auto crlf = string.find("\r\n"); + + if (crlf != String::npos) { + auto stream = std::istringstream(string.substr(0, crlf)); + String uri; + stream + >> this->method + >> uri + >> this->version; + + this->url = uri; + this->headers = string.substr(crlf, string.find("\r\n\r\n")); + this->body.set( + input + string.find("\r\n\r\n") + 4, + 0, + size - string.find("\r\n\r\n") - 4 + ); + } + } + + bool Request::valid () const { + return ( + this->method.size() > 0 && + this->version.size() > 0 && + this->url.href.size() > 0 + ); + } + + String Request::str () const { + return this->method + " " + this->url.pathname + this->url.search + " HTTP/" + this->version + "\r\n" + this->headers.str(); + } +} diff --git a/src/runtime/http/response.cc b/src/runtime/http/response.cc new file mode 100644 index 0000000000..34eb66bbed --- /dev/null +++ b/src/runtime/http/response.cc @@ -0,0 +1,38 @@ +#include "../http.hh" + +namespace ssc::runtime::http { + Response::Response (const String& input) + : body(input) + {} + + Response::Response (const Headers& headers) + : headers(headers) + {} + + Response::Response (const Status& status) + : status(status) + {} + + Response::Response (const Status& status, const Headers& headers) + : headers(headers), + status(status) + {} + + Response& Response::setHeader (const Headers::Header& header) { + this->headers.set(header); + return *this; + } + + Response& Response::setHeader (const String& key, const String& value) { + this->headers.set(key, value); + return *this; + } + + const unsigned char* Response::data () const { + return this->body.data(); + } + + String Response::str () const { + return this->status.str() + "\r\n" + this->headers.str(); + } +} diff --git a/src/runtime/http/status.cc b/src/runtime/http/status.cc new file mode 100644 index 0000000000..53bf3a8e6d --- /dev/null +++ b/src/runtime/http/status.cc @@ -0,0 +1,137 @@ +#include "../http.hh" +#include "../json.hh" +#include "../string.hh" + +using ssc::runtime::string::toProperCase; +using ssc::runtime::string::toLowerCase; +using ssc::runtime::string::trim; +using ssc::runtime::string::split; + +namespace ssc::runtime::http { + static const Map statuses = { + {100, {100, "Continue"}}, + {101, {101, "Switching Protocols"}}, + {102, {102, "Processing"}}, + {103, {103, "Early Hints"}}, + {200, {200, "OK"}}, + {201, {201, "Created"}}, + {202, {202, "Accepted"}}, + {203, {203, "Non-Authoritative Information"}}, + {204, {204, "No Content"}}, + {205, {205, "Reset Content"}}, + {206, {206, "Partial Content"}}, + {207, {207, "Multi-Status"}}, + {208, {208, "Already Reported"}}, + {226, {226, "IM Used"}}, + {300, {300, "Multiple Choices"}}, + {301, {301, "Moved Permanently"}}, + {302, {302, "Found"}}, + {303, {303, "See Other"}}, + {304, {304, "Not Modified"}}, + {305, {305, "Use Proxy"}}, + {307, {307, "Temporary Redirect"}}, + {308, {308, "Permanent Redirect"}}, + {400, {400, "Bad Request"}}, + {401, {401, "Unauthorized"}}, + {402, {402, "Payment Required"}}, + {403, {403, "Forbidden"}}, + {404, {404, "Not Found"}}, + {405, {405, "Method Not Allowed"}}, + {406, {406, "Not Acceptable"}}, + {407, {407, "Proxy Authentication Required"}}, + {408, {408, "Request Timeout"}}, + {409, {409, "Conflict"}}, + {410, {410, "Gone"}}, + {411, {411, "Length Required"}}, + {412, {412, "Precondition Failed"}}, + {413, {413, "Payload Too Large"}}, + {414, {414, "URI Too Long"}}, + {415, {415, "Unsupported Media Type"}}, + {416, {416, "Range Not Satisfiable"}}, + {417, {417, "Expectation Failed"}}, + {418, {418, "I'm a Teapot"}}, + {421, {421, "Misdirected Request"}}, + {422, {422, "Unprocessable Entity"}}, + {423, {423, "Locked"}}, + {424, {424, "Failed Dependency"}}, + {425, {425, "Too Early"}}, + {426, {426, "Upgrade Required"}}, + {428, {428, "Precondition Required"}}, + {429, {429, "Too Many Requests"}}, + {431, {431, "Request Header Fields Too Large"}}, + {451, {451, "Unavailable For Legal Reasons"}}, + {500, {500, "Internal Server Error"}}, + {501, {501, "Not Implemented"}}, + {502, {502, "Bad Gateway"}}, + {503, {503, "Service Unavailable"}}, + {504, {504, "Gateway Timeout"}}, + {505, {505, "HTTP Version Not Supported"}}, + {506, {506, "Variant Also Negotiates"}}, + {507, {507, "Insufficient Storage"}}, + {508, {508, "Loop Detected"}}, + {509, {509, "Bandwidth Limit Exceeded"}}, + {510, {510, "Not Extended"}}, + {511, {511, "Network Authentication Required"}} + }; + + static String normalizeStatusText (const String& statusText) { + StringStream output; + for (const auto& word : split(trim(statusText), ' ')) { + output << toProperCase(word) << " "; + } + return trim(output.str()); + } + + String getStatusText (int code) { + if (statuses.contains(code)) { + return statuses.at(code).text; + } + + return ""; + } + + int getStatusCode (const String& text) { + for (const auto& entry : statuses) { + if (text == entry.second.text) { + return entry.second.code; + } + + if (toLowerCase(text) == toLowerCase(entry.second.text)) { + return entry.second.code; + } + } + return 0; + } + + const Map& getStatusMap () { + return statuses; + } + + Status::Status (int code, const String& text) + : code(code), + text(text) + {} + + Status::Status (int code) + : code(code), + text(getStatusText(code)) + {} + + Status::Status (const String& text) + : code(getStatusCode(text)), + text(text) + {} + + String Status::str () const { + StringStream output; + output << this->code << " " << normalizeStatusText(this->text); + return trim(output.str()); + } + + JSON::Object Status::json () const { + return JSON::Object::Entries { + {"code", this->code}, + {"text", normalizeStatusText(this->text)} + }; + } +} diff --git a/src/runtime/ini.hh b/src/runtime/ini.hh new file mode 100644 index 0000000000..9fcf6f7b64 --- /dev/null +++ b/src/runtime/ini.hh @@ -0,0 +1,12 @@ +#ifndef SOCKET_RUNTIME_RUNTIME_INI_H +#define SOCKET_RUNTIME_RUNTIME_INI_H + +#include "platform.hh" + +namespace ssc::runtime::INI { + using Map = Map; + Map parse (const String& source); + Map parse (const String& source, const String& keyPathSeparator); + String serialize (const Map&); +} +#endif diff --git a/src/core/ini.cc b/src/runtime/ini/parse.cc similarity index 90% rename from src/core/ini.cc rename to src/runtime/ini/parse.cc index f454f832c9..b12646132f 100644 --- a/src/core/ini.cc +++ b/src/runtime/ini/parse.cc @@ -1,15 +1,20 @@ -#include "../platform/platform.hh" -#include "ini.hh" +#include "../ini.hh" +#include "../string.hh" -namespace SSC::INI { - Map parse (const String& source) { +using namespace ssc::runtime::string; + +namespace ssc::runtime::INI { + INI::Map parse (const String& source) { return parse(source, "_"); } - Map parse (const String& source, const String& keyPathSeparator) { + INI::Map parse ( + const String& source, + const String& keyPathSeparator + ) { Vector entries = split(source, '\n'); String prefix = ""; - Map settings = {}; + INI::Map settings = {}; for (auto entry : entries) { entry = trim(entry); diff --git a/src/runtime/ini/serialize.cc b/src/runtime/ini/serialize.cc new file mode 100644 index 0000000000..52b3c59f75 --- /dev/null +++ b/src/runtime/ini/serialize.cc @@ -0,0 +1,14 @@ +#include "../ini.hh" +#include "../string.hh" + +using namespace ssc::runtime::string; + +namespace ssc::runtime::INI { + String serialize (const Map& map) { + StringStream stream; + for (const auto& entry : map) { + stream << entry.first << " = " << entry.second << "\n"; + } + return trim(stream.str()); + } +} diff --git a/src/core/io.cc b/src/runtime/io.hh similarity index 62% rename from src/core/io.cc rename to src/runtime/io.hh index f2a014a961..e552c6034b 100644 --- a/src/core/io.cc +++ b/src/runtime/io.hh @@ -1,13 +1,15 @@ +#ifndef SOCKET_RUNTIME_IO_H +#define SOCKET_RUNTIME_IO_H + #include -#include "../platform/platform.hh" #include "../cli/cli.hh" +#include "platform.hh" #include "env.hh" -#include "io.hh" -namespace SSC::IO { - void write (const String& input, bool isErrorOutput) { - static const auto GITHUB_ACTIONS_CI = Env::get("GITHUB_ACTIONS_CI"); +namespace ssc::runtime::io { + inline void write (const String& input, bool isErrorOutput) { + static const auto GITHUB_ACTIONS_CI = env::get("GITHUB_ACTIONS_CI"); static const auto isGitHubActionsCI = GITHUB_ACTIONS_CI.size() > 0; auto& stream = isErrorOutput ? std::cerr : std::cout; @@ -23,6 +25,7 @@ namespace SSC::IO { #endif stream << std::endl; - CLI::notify(); + cli::notify(); } } +#endif diff --git a/src/runtime/ipc.hh b/src/runtime/ipc.hh new file mode 100644 index 0000000000..2845d126bd --- /dev/null +++ b/src/runtime/ipc.hh @@ -0,0 +1,212 @@ +#ifndef SOCKET_RUNTIME_IPC_H +#define SOCKET_RUNTIME_IPC_H + +#include "webview/preload.hh" +#include "queued_response.hh" +#include "unique_client.hh" +#include "crypto.hh" +#include "bytes.hh" +#include "core.hh" +#include "http.hh" +#include "url.hh" + +namespace ssc::runtime::ipc { + // forward + class IBridge; + + /** + * A `Client` that represents a unique caller of the IPC channel. + */ + struct Client : public UniqueClient { + using UniqueClient::UniqueClient; + + Client (const UniqueClient& client) + : UniqueClient(client) + {} + }; + + struct MessageCancellation { + void (*handler)(void*) = nullptr; + void* data = nullptr; + }; + + class Message { + public: + using Seq = String; + + bytes::BufferQueue buffer; + Client client; + URL uri; + + String value = ""; + String name = ""; + int index = -1; + Seq seq = ""; + + bool isHTTP = false; + + SharedPointer cancel = nullptr; + + Message () = default; + Message (const Message& message); + Message (Message&& message); + Message (const String& source, bool decodeValues); + Message (const String& source); + + Message& operator = (const Message&); + Message& operator = (Message&&); + + bool has (const String& key) const; + bool contains (const String& key) const; + const String& at (const String& key) const; + const String get (const String& key) const; + const String get (const String& key, const String& fallback) const; + const Map dump () const; + const String str () const; + const char* c_str () const; + const Map map () const; + const JSON::Object json () const; + }; + + class Result { + public: + class Err { + public: + Message message; + Message::Seq seq; + JSON::Any value; + Err () = default; + Err (const Message&, const char*); + Err (const Message&, const String&); + Err (const Message&, const JSON::Any&); + }; + + class Data { + public: + Message message; + Message::Seq seq; + JSON::Any value; + QueuedResponse queuedResponse; + Data () = default; + Data (const Message&, const JSON::Any&); + Data (const Message&, const JSON::Any&, const QueuedResponse&); + }; + + Message message; + Message::Seq seq = "-1"; + uint64_t id = rand64(); + String source = ""; + String token = ""; + JSON::Any value = nullptr; + JSON::Any data = nullptr; + JSON::Any err = nullptr; + http::Headers headers; + QueuedResponse queuedResponse; + + Result () = default; + Result (const Result&) = default; + Result (const JSON::Any&, const String& token = ""); + Result (const Err& error); + Result (const Data& data); + Result (const Message::Seq&, const Message&); + Result (const Message::Seq&, const Message&, const JSON::Any&); + Result ( + const Message::Seq&, + const Message&, + const JSON::Any&, + const QueuedResponse& + ); + + const String str () const; + const JSON::Any json () const; + }; + + class Router { + public: + using ReplyCallback = Function; + using ResultCallback = Function; + using MessageCallback = Function; + + struct MessageCallbackContext { + bool async = true; + MessageCallback callback; + }; + + struct MessageCallbackListenerContext { + uint64_t token; + MessageCallback callback; + }; + + using Table = Map; + using Listeners = Map>; + + private: + Table preserved; + + public: + context::Dispatcher& dispatcher; + IBridge& bridge; + + Listeners listeners; + Mutex mutex; + Table table; + + Router (IBridge&); + + // the `Router` instance is strictly owned and cannot be copied or moved + Router () = delete; + Router (const Router&) = delete; + Router (const Router&&) = delete; + Router (Router&&) = delete; + Router& operator = (const Router&) = delete; + Router& operator = (Router&&) = delete; + + void init (); + void mapRoutes (); + void preserveCurrentTable (); + uint64_t listen (const String& name, const MessageCallback callback); + bool unlisten (const String& name, uint64_t token); + void map (const String& name, const MessageCallback callback); + void map (const String& name, bool async, const MessageCallback callback); + void unmap (const String& name); + bool invoke (const String& uri, const ResultCallback callback); + bool invoke (const String& uri, SharedPointer bytes, size_t size); + bool invoke (const String&, SharedPointer, size_t, const ResultCallback); + bool invoke (const Message&, SharedPointer, size_t, const ResultCallback); + }; + + /** + * The `Bridge` interface for an IPC channel. + */ + class IBridge { + public: + context::RuntimeContext& context; + context::Dispatcher& dispatcher; + Client client; + Router router; + + IBridge ( + context::Dispatcher& dispatcher, + context::RuntimeContext& context, + const Client& client + ) : dispatcher(dispatcher), + context(context), + client(client), + router(*this) + {} + + virtual bool active () const = 0; + virtual bool emit (const String&, const String& = "") = 0; + virtual bool emit (const String&, const JSON::Any& = {}) = 0; + virtual bool send (const Message::Seq&, const String&, const QueuedResponse& = {}) = 0; + virtual bool send (const Message::Seq& seq, const JSON::Any& json, const QueuedResponse& = {}) = 0; + virtual bool route ( const String&, SharedPointer, size_t) = 0; + virtual bool route (const String&, SharedPointer, size_t, const Router::ResultCallback) = 0; + }; + +} +#endif diff --git a/src/runtime/ipc/message.cc b/src/runtime/ipc/message.cc new file mode 100644 index 0000000000..fca4f11ebd --- /dev/null +++ b/src/runtime/ipc/message.cc @@ -0,0 +1,155 @@ +#include "../bytes.hh" +#include "../url.hh" +#include "../ipc.hh" + +using ssc::runtime::url::decodeURIComponent; +using ssc::runtime::bytes::BufferQueue; + +namespace ssc::runtime::ipc { + Message::Message (const Message& message) + : value(message.value), + index(message.index), + name(message.name), + seq(message.seq), + uri(message.uri), + isHTTP(message.isHTTP), + cancel(message.cancel), + buffer(message.buffer), + client(message.client) + {} + + Message::Message (const String& source) + : Message(source, false) + {} + + Message::Message (const String& source, bool decodeValues) + : uri(source, decodeValues) + { + this->seq = this->get("seq"); + this->name = this->uri.hostname; + this->value = this->get("value"); + + if (this->uri.searchParams.contains("index")) { + try { + this->index = this->uri.searchParams + .get("index") + .as() + .value(); + } catch (const Exception& e) { + debug( + "ssc::runtime::ipc::Message: Warning: received non-integer index: %s: %s", + this->uri.c_str(), + e.what() + ); + } + } + } + + Message::Message (Message&& msg) { + this->buffer = std::move(msg.buffer); + this->client = std::move(msg.client); + this->index = msg.index; + this->value = std::move(msg.value); + this->name = std::move(msg.name); + this->uri = std::move(msg.uri); + this->seq = std::move(msg.seq); + this->isHTTP = msg.isHTTP; + this->cancel = std::move(msg.cancel); + + msg.name = ""; + msg.index = -1; + msg.value = ""; + msg.uri = URL(); + msg.seq = ""; + msg.isHTTP = false; + msg.buffer.reset(); + msg.cancel = nullptr; + } + + Message& Message::operator = (const Message& msg) { + this->buffer = msg.buffer; + this->client = msg.client; + this->index = msg.index; + this->value = msg.value; + this->name = msg.name; + this->uri = msg.uri; + this->seq = msg.seq; + this->isHTTP = msg.isHTTP; + this->cancel = std::move(msg.cancel); + return *this; + } + + Message& Message::operator = (Message&& msg) { + this->buffer = std::move(msg.buffer); + this->client = std::move(msg.client); + this->index = msg.index; + this->value = std::move(msg.value); + this->name = std::move(msg.name); + this->uri = std::move(msg.uri); + this->seq = std::move(msg.seq); + this->isHTTP = msg.isHTTP; + this->cancel = std::move(msg.cancel); + + msg.name = ""; + msg.index = -1; + msg.value = ""; + msg.uri = URL(); + msg.seq = ""; + msg.isHTTP = false; + msg.buffer.reset(); + msg.cancel = nullptr; + return *this; + } + + bool Message::has (const String& key) const { + return this->uri.searchParams.contains(key); + } + + bool Message::contains (const String& key) const { + return this->uri.searchParams.contains(key); + } + + const String& Message::at (const String& key) const { + return this->uri.searchParams.at(key).data; + } + + const String Message::get (const String& key) const { + return this->get(key, ""); + } + + const String Message::get (const String& key, const String &fallback) const { + if (key == "value" && this->value.size() > 0) { + return this->value; + } + + return this->contains(key) + ? decodeURIComponent(this->uri.searchParams.get(key).str()) + : fallback; + } + + const Map Message::dump () const { + return this->map(); + } + + const String Message::str () const { + return this->uri.str(); + } + + const char* Message::c_str () const { + return this->uri.c_str(); + } + + const Map Message::map () const { + return this->uri.searchParams.map(); + } + + const JSON::Object Message::json () const { + return JSON::Object::Entries { + {"name", this->name}, + {"value", this->value}, + {"index", this->index}, + {"seq", this->seq}, + {"data", this->map()} + }; + } +} diff --git a/src/ipc/message.kt b/src/runtime/ipc/message.kt similarity index 100% rename from src/ipc/message.kt rename to src/runtime/ipc/message.kt diff --git a/src/ipc/result.cc b/src/runtime/ipc/result.cc similarity index 62% rename from src/ipc/result.cc rename to src/runtime/ipc/result.cc index 5a7123251f..1dae72b8c0 100644 --- a/src/ipc/result.cc +++ b/src/runtime/ipc/result.cc @@ -1,33 +1,33 @@ -#include "result.hh" - -namespace SSC::IPC { - Result::Result (const Message::Seq& seq, const Message& message) { - this->id = rand64(); - this->seq = seq; - this->message = message; - this->source = message.name; +#include "../ipc.hh" + +namespace ssc::runtime::ipc { + Result::Result (const Message::Seq& seq, const Message& message) + : message(message), + source(message.name), + seq(seq) + { this->token = message.get("ipc-token", ""); - this->post.workerId = this->message.get("runtime-worker-id"); + this->queuedResponse.workerId = this->message.get("runtime-worker-id"); } Result::Result ( const Message::Seq& seq, const Message& message, - JSON::Any value - ) : Result(seq, message, value, Post{}) + const JSON::Any& value + ) : Result(seq, message, value, QueuedResponse{}) {} Result::Result ( const Message::Seq& seq, const Message& message, - JSON::Any value, - Post post + const JSON::Any& value, + const QueuedResponse& queuedResponse ) : Result(seq, message) { - this->post = post; - this->headers = Headers(post.headers); + this->queuedResponse = queuedResponse; + this->headers = http::Headers(queuedResponse.headers); - if (this->post.workerId.size() == 0) { - this->post.workerId = this->message.get("runtime-worker-id"); + if (this->queuedResponse.workerId.size() == 0) { + this->queuedResponse.workerId = this->message.get("runtime-worker-id"); } if (value.type != JSON::Type::Any) { @@ -35,32 +35,33 @@ namespace SSC::IPC { } } - Result::Result (const JSON::Any value, const String& token) { - this->id = rand64(); - this->value = value; - this->token = token; - } + Result::Result (const JSON::Any& value, const String& token) + : value(value), + token(token) + {} - Result::Result (const Err error): Result(error.message.seq, error.message) { + Result::Result (const Err& error) + : Result(error.message.seq, error.message) + { this->err = error.value; this->token = error.message.get("ipc-token", ""); - this->source = error.message.name; } - Result::Result (const Data data): Result(data.message.seq, data.message) { + Result::Result (const Data& data) + : Result(data.message.seq, data.message) + { this->data = data.value; - this->post = data.post; + this->queuedResponse = data.queuedResponse; this->token = data.message.get("ipc-token", ""); - this->source = data.message.name; - this->headers = Headers(data.post.headers); + this->headers = http::Headers(data.queuedResponse.headers); } - JSON::Any Result::json () const { + const JSON::Any Result::json () const { if (!this->value.isNull()) { if (this->value.isObject()) { auto object = this->value.as(); - if (object.has("data") || object.has("err")) { + if (object.has("source") && (object.has("data") || object.has("err"))) { object["source"] = this->source; object["token"] = this->token.size() > 0 ? JSON::Any(JSON::String(this->token)) @@ -118,12 +119,16 @@ namespace SSC::IPC { return JSON::Object(entries); } - String Result::str () const { + const String Result::str () const { auto json = this->json(); return json.str(); } - Result::Err::Err (const Message& message, JSON::Any value) { + Result::Err::Err (const Message& message, const JSON::Any& value) + : message(message), + value(value), + seq(message.seq) + { this->seq = message.seq; this->message = message; this->value = value; @@ -133,20 +138,24 @@ namespace SSC::IPC { : Err(message, String(error)) {} - Result::Err::Err (const Message& message, const String& error) { - this->seq = message.seq; - this->message = message; + Result::Err::Err (const Message& message, const String& error) + : message(message), + seq(message.seq) + { this->value = JSON::Object::Entries {{"message", error}}; } - Result::Data::Data (const Message& message, JSON::Any value) - : Data(message, value, Post{}) + Result::Data::Data (const Message& message, const JSON::Any& value) + : Data(message, value, QueuedResponse{}) {} - Result::Data::Data (const Message& message, JSON::Any value, Post post) { - this->seq = message.seq; - this->message = message; - this->value = value; - this->post = post; - } + Result::Data::Data ( + const Message& message, + const JSON::Any& value, + const QueuedResponse& queuedResponse + ) : message(message), + value(value), + queuedResponse(queuedResponse), + seq(message.seq) + {} } diff --git a/src/ipc/router.cc b/src/runtime/ipc/router.cc similarity index 62% rename from src/ipc/router.cc rename to src/runtime/ipc/router.cc index e158fe352b..b3e9e95398 100644 --- a/src/ipc/router.cc +++ b/src/runtime/ipc/router.cc @@ -1,10 +1,12 @@ -#include "bridge.hh" -#include "router.hh" -#include "../core/trace.hh" +#include "../string.hh" +#include "../ipc.hh" -namespace SSC::IPC { - Router::Router (Bridge* bridge) - : bridge(bridge) +using ssc::runtime::string::toLowerCase; + +namespace ssc::runtime::ipc { + Router::Router (IBridge& bridge) + : dispatcher(bridge.dispatcher), + bridge(bridge) {} void Router::init () { @@ -18,7 +20,7 @@ namespace SSC::IPC { uint64_t Router::listen ( const String& name, - const MessageCallback& callback + const MessageCallback callback ) { const auto key = toLowerCase(name); @@ -50,14 +52,14 @@ namespace SSC::IPC { return false; } - void Router::map (const String& name, const MessageCallback& callback) { - return this->map(name, true, callback); + void Router::map (const String& name, const MessageCallback callback) { + return this->map(name, true, std::move(callback)); } void Router::map ( const String& name, bool async, - const MessageCallback& callback + const MessageCallback callback ) { if (callback != nullptr) { const auto key = toLowerCase(name); @@ -78,13 +80,13 @@ namespace SSC::IPC { size_t size ) { return this->invoke(uri, bytes, size, [this](auto result) { - this->bridge->dispatch([=, this] () { - this->bridge->send(result.seq, result.str(), result.post); + this->dispatcher.dispatch([=, this] () { + this->bridge.send(result.seq, result.str(), result.queuedResponse); }); }); } - bool Router::invoke (const String& uri, const ResultCallback& callback) { + bool Router::invoke (const String& uri, const ResultCallback callback) { return this->invoke(uri, nullptr, 0, callback); } @@ -92,23 +94,23 @@ namespace SSC::IPC { const String& uri, SharedPointer bytes, size_t size, - const ResultCallback& callback + const ResultCallback callback ) { - if (this->bridge->core->isShuttingDown) { + if (!this->bridge.active()) { return false; } const auto message = Message(uri, true); - return this->invoke(message, bytes, size, callback); + return this->invoke(std::move(message), bytes, size, std::move(callback)); } bool Router::invoke ( const Message& message, SharedPointer bytes, size_t size, - const ResultCallback& callback + const ResultCallback callback ) { - if (this->bridge->core->isShuttingDown) { + if (!this->bridge.active()) { return false; } @@ -129,46 +131,51 @@ namespace SSC::IPC { return false; } - Message msg(message); + auto incomingMessage = Message(message); if (bytes != nullptr && size > 0) { - msg.buffer.bytes = bytes; - msg.buffer.size = size; + incomingMessage.buffer.reset(bytes, size); } // named listeners if (this->listeners.contains(name)) { - const auto listeners = this->listeners[name]; + const auto& listeners = this->listeners[name]; for (const auto& listener : listeners) { - listener.callback(msg, this, [](const auto& _) {}); + listener.callback(incomingMessage, this, [](const auto& _) {}); } } // wild card (*) listeners if (this->listeners.contains("*")) { - const auto listeners = this->listeners["*"]; + const auto& listeners = this->listeners["*"]; for (const auto& listener : listeners) { - listener.callback(msg, this, [](const auto& _) {}); + listener.callback(incomingMessage, this, [](const auto& _) {}); } } if (context.async) { - return this->bridge->dispatch([=, this]() mutable { - context.callback(msg, this, [callback, this](const auto result) mutable { + return this->dispatcher.dispatch([ + this, + context = std::move(context), + callback = std::move(callback), + incomingMessage = std::move(incomingMessage) + ]() mutable { + context.callback(incomingMessage, this, [this, callback](const auto result) mutable { if (result.seq == "-1") { - this->bridge->send(result.seq, result.str(), result.post); + this->bridge.send(result.seq, result.str(), result.queuedResponse); } else { - this->bridge->dispatch([result, callback, this]() { - callback(result); - }); + callback(result); } }); }); } - context.callback(msg, this, [=, this](const auto result) mutable { + context.callback(incomingMessage, this, [ + this, + callback = std::move(callback) + ](const auto result) mutable { if (result.seq == "-1") { - this->bridge->send(result.seq, result.str(), result.post); + this->bridge.send(result.seq, result.str(), result.queuedResponse); } else { callback(result); } diff --git a/src/ipc/routes.cc b/src/runtime/ipc/routes.cc similarity index 94% rename from src/ipc/routes.cc rename to src/runtime/ipc/routes.cc index 461f1b502a..e3b0b06959 100644 --- a/src/ipc/routes.cc +++ b/src/runtime/ipc/routes.cc @@ -1,10 +1,12 @@ -#include "../app/app.hh" -#include "../cli/cli.hh" -#include "../core/json.hh" -#include "../core/resource.hh" -#include "../core/headers.hh" #include "../extension/extension.hh" #include "../window/window.hh" +#include "../core/resource.hh" +#include "../core/headers.hh" +#include "../core/json.hh" +#include "../core/url.hh" +#include "../app/app.hh" +#include "../cli/cli.hh" + #include "ipc.hh" extern int LLAMA_BUILD_NUMBER; @@ -444,6 +446,101 @@ static void mapIPCRoutes (Router *router) { ); }); + router->map("broadcast_channel.subscribe", [=](auto message, auto router, auto reply) { + auto err = validateMessageParameters(message, { + "name", + "origin" + }); + + if (err.type != JSON::Type::Null) { + return reply(Result::Err { message, err }); + } + + const auto name = message.get("name"); + const auto origin = message.get("origin"); + const auto bridge = router->bridge; + const auto subscription = router->bridge->core->broadcastChannel.subscribe({ + name, + origin, + router->bridge->client, + [message, bridge](const auto event) { + const auto app = App::sharedApplication(); + const auto window = app->windowManager.getWindowForBridge(bridge); + if (window) { + bridge->emit("broadcastchannelmessage", JSON::Object::Entries { + {"source", message.name}, + {"data", event.json()}, + {"err", nullptr} + }); + } + } + }); + + reply(Result::Data { message, subscription.json() }); + }); + + router->map("broadcast_channel.unsubscribe", [=](auto message, auto router, auto reply) { + auto err = validateMessageParameters(message, { "id" }); + + if (err.type != JSON::Type::Null) { + return reply(Result::Err { message, err }); + } + + uint64_t id; + REQUIRE_AND_GET_MESSAGE_VALUE(id, "id", std::stoull); + + if (!router->bridge->core->broadcastChannel.unsubscribe(id)) { + const auto err = JSON::Object::Entries { + {"type", "NotFoundError"}, + {"message", "No subscription found" } + }; + return reply(Result::Err { message, err }); + } + + reply(Result { message.seq, message, JSON::Object {} }); + }); + + router->map("broadcast_channel.postMessage", [=](auto message, auto router, auto reply) { + auto err = validateMessageParameters(message, { + "origin", + "token", + "name", + "data" + }); + + if (err.type != JSON::Type::Null) { + return reply(Result::Err { message, err }); + } + + const auto data = JSON::Raw(message.get("data")); + const auto name = message.get("name"); + const auto token = message.get("token"); + const auto origin = message.get("origin"); + const auto messageToPost = Core::BroadcastChannel::Message { + Core::BroadcastChannel::Client { + router->bridge->client.id, + router->bridge->client.index + }, + origin, + token, + name, + data + }; + + const auto posted = router->bridge->core->broadcastChannel.postMessage(messageToPost); + + if (!posted) { + const auto err = JSON::Object::Entries { + {"type", "NotFoundError"}, + {"message", "No subscribers"} + }; + + return reply(Result::Err { message, err }); + } + + reply(Result::Data { message, messageToPost.json() }); + }); + /** * Kills an already spawned child process. * @@ -1696,12 +1793,55 @@ static void mapIPCRoutes (Router *router) { reply(Result::Data { message, JSON::Object::Entries { + {"sharedKey", router->bridge->core->conduit.sharedKey}, {"isActive", router->bridge->core->conduit.isActive()}, {"port", router->bridge->core->conduit.port.load()} } }); }); + /** + * A private API for setting the shared key of the Runtime `Core::Conduit. + */ + router->map("internal.conduit.setSharedKey", [=](auto message, auto router, auto reply) { + auto err = validateMessageParameters(message, {"sharedKey"}); + + if (err.type != JSON::Type::Null) { + return reply(Result::Err { message, err }); + } + + const auto sharedKey = message.get("sharedKey"); + + if (sharedKey.size() < 8) { + const auto err = JSON::Object::Entries { + {"message", "Invalid shared key length. Must be at least 8 bytes"} + }; + + return reply(Result::Err { message, err }); + } + + router->bridge->core->conduit.sharedKey = sharedKey; + + reply(Result::Data { + message, + JSON::Object::Entries { + {"sharedKey", sharedKey} + } + }); + }); + + /** + * A private API for getting the shared key of the Runtime `Core::Conduit. + */ + router->map("internal.conduit.getSharedKey", [=](auto message, auto router, auto reply) { + reply(Result::Data { + message, + JSON::Object::Entries { + {"sharedKey", router->bridge->core->conduit.sharedKey} + } + }); + }); + /** * Log `value to stdout` with platform dependent logger. * @param value @@ -2283,6 +2423,54 @@ static void mapIPCRoutes (Router *router) { reply(Result { message.seq, message, JSON::Null() }); }); + /** + * Sets an evironment variable + * @param key + * @param value + */ + router->map("process.env.set", [=](auto message, auto router, auto reply) { + const auto err = validateMessageParameters(message, {"key", "value"}); + + if (err.type != JSON::Type::Null) { + return reply(Result::Err { message, err }); + } + + const auto key = message.get("key"); + const auto value = message.get("value"); + Env::set(key, value); + reply(Result { + message.seq, + message, + JSON::Object::Entries { + {"key", key}, + {"value", value} + } + }); + }); + + /** + * Gets an evironment variable + * @param key + */ + router->map("process.env.get", [=](auto message, auto router, auto reply) { + const auto err = validateMessageParameters(message, {"key"}); + + if (err.type != JSON::Type::Null) { + return reply(Result::Err { message, err }); + } + + const auto key = message.get("key"); + const auto value = Env::get(key); + reply(Result { + message.seq, + message, + JSON::Object::Entries { + {"key", key}, + {"value", value} + } + }); + }); + /** * Prints incoming message value to stdout. * @param value @@ -2297,10 +2485,10 @@ static void mapIPCRoutes (Router *router) { if (Env::get("SSC_LOG_SOCKET").size() > 0) { Core::UDP::SendOptions options; options.size = 2; - options.bytes = SharedPointer(new char[3]{ '+', 'N', '\0' }); options.address = "0.0.0.0"; options.port = std::stoi(Env::get("SSC_LOG_SOCKET")); options.ephemeral = true; + options.bytes.reset(new char[3]{ '+', 'N', '\0' }); router->bridge->core->udp.send("-1", 0, options, [](auto seq, auto json, auto post) {}); } #endif @@ -2330,10 +2518,10 @@ static void mapIPCRoutes (Router *router) { if (Env::get("SSC_LOG_SOCKET").size() > 0) { Core::UDP::SendOptions options; options.size = 2; - options.bytes = SharedPointer(new char[3]{ '+', 'N', '\0' }); options.address = "0.0.0.0"; options.port = std::stoi(Env::get("SSC_LOG_SOCKET")); options.ephemeral = true; + options.bytes.reset(new char[3]{ '+', 'N', '\0' }); router->bridge->core->udp.send("-1", 0, options, [](auto seq, auto json, auto post) {}); } #endif @@ -2360,11 +2548,12 @@ static void mapIPCRoutes (Router *router) { const auto options = ServiceWorkerContainer::RegistrationOptions { .type = ServiceWorkerContainer::RegistrationOptions::Type::Module, .scope = message.get("scope"), - .scriptURL = message.get("scriptURL") + .scriptURL = message.get("scriptURL"), + .serializedWorkerArgs = encodeURIComponent(message.get("__runtime_worker_args")) }; const auto registration = router->bridge->navigator.serviceWorker.registerServiceWorker(options); - auto json = JSON::Object { + const auto json = JSON::Object { JSON::Object::Entries { {"registration", registration.json()} } @@ -2951,7 +3140,7 @@ static void mapIPCRoutes (Router *router) { router->bridge->core->udp.readStart( message.seq, id, - [&, id, router, message, reply](auto seq, auto json, auto post) { + [id, router, message, reply](auto seq, auto json, auto post) { if (seq == "-1" && router->bridge->core->conduit.has(id)) { auto data = json["data"]; @@ -2961,8 +3150,10 @@ static void mapIPCRoutes (Router *router) { }; auto client = router->bridge->core->conduit.get(id); - client->send(options, post.body, post.length); - return; + if (client) { + client->send(options, post.body, post.length); + return; + } } reply(Result { seq, message, json, post }); @@ -3186,7 +3377,7 @@ static void mapIPCRoutes (Router *router) { */ router->map("window.create", [=](auto message, auto router, auto reply) { const auto app = App::sharedApplication(); - auto err = validateMessageParameters(message, {"targetWindowIndex"}); + const auto err = validateMessageParameters(message, {"targetWindowIndex"}); if (app == nullptr) { return reply(Result::Err { message, "Application is invalid state" }); @@ -3284,10 +3475,11 @@ static void mapIPCRoutes (Router *router) { options.backgroundColorDark = message.get("backgroundColorDark"); options.utility = message.get("utility") == "true" ? true : false; options.debug = message.get("debug") == "true" ? true : false; - options.userScript = message.get("userScript"); options.index = targetWindowIndex; options.RUNTIME_PRIMORDIAL_OVERRIDES = message.get("__runtime_primordial_overrides__"); options.userConfig = INI::parse(message.get("config")); + options.resourcesDirectory = message.get("resourcesDirectory"); + options.shouldPreferServiceWorker = message.get("shouldPreferServiceWorker", "false") == "true"; if (options.index >= SOCKET_RUNTIME_MAX_WINDOWS) { options.features.useGlobalCommonJS = false; @@ -3388,6 +3580,43 @@ static void mapIPCRoutes (Router *router) { }); }); + /** + * Gets the current state of a window + * @param index + */ + router->map("window", [=](auto message, auto router, auto reply) { + const auto app = App::sharedApplication(); + auto err = validateMessageParameters(message, {"index"}); + + if (app == nullptr) { + return reply(Result::Err { message, "Application is invalid state" }); + } + + if (err.type != JSON::Type::Null) { + return reply(Result::Err { message, err }); + } + + int index; + REQUIRE_AND_GET_MESSAGE_VALUE(index, "index", std::stoi); + + app->dispatch([=]() { + const auto window = app->windowManager.getWindow(index); + const auto windowStatus = app->windowManager.getWindowStatus(index); + + if (!window || windowStatus == WindowManager::WindowStatus::WINDOW_NONE) { + return reply(Result::Err { + message, + JSON::Object::Entries { + {"message", "Target window not found"}, + {"type", "NotFoundError"} + } + }); + } + + reply(Result::Data { message, window->json() }); + }); + }); + /** * Hides a target window * @param targetWindowIndex diff --git a/src/runtime/javascript.hh b/src/runtime/javascript.hh new file mode 100644 index 0000000000..5555cb85af --- /dev/null +++ b/src/runtime/javascript.hh @@ -0,0 +1,35 @@ +#ifndef SOCKET_RUNTIME_JAVASCRIPT_H +#define SOCKET_RUNTIME_JAVASCRIPT_H + +#include "platform.hh" +#include "json.hh" + +namespace ssc::runtime::javascript { + String createJavaScript (const String& name, const String& source); + + String getEmitToRenderProcessJavaScript ( + const String& event, + const String& value, + const String& target, + const JSON::Object& options + ); + + String getEmitToRenderProcessJavaScript ( + const String& event, + const String& value + ); + + String getResolveMenuSelectionJavaScript ( + const String& seq, + const String& title, + const String& parent, + const String type = "system" + ); + + String getResolveToRenderProcessJavaScript ( + const String& seq, + const String& state, + const String& value + ); +} +#endif diff --git a/src/core/javascript.cc b/src/runtime/javascript/factory.cc similarity index 98% rename from src/core/javascript.cc rename to src/runtime/javascript/factory.cc index bcdfde2f5e..7156b039fa 100644 --- a/src/core/javascript.cc +++ b/src/runtime/javascript/factory.cc @@ -1,7 +1,9 @@ -#include "core.hh" -#include "json.hh" +#include "../javascript.hh" +#include "../string.hh" -namespace SSC { +using namespace ssc::runtime::string; + +namespace ssc::runtime::javascript { String createJavaScript (const String& name, const String& source) { return String( ";(async () => { \n" @@ -42,7 +44,7 @@ namespace SSC { const String& target, const JSON::Object& options ) { - SSC::String jsonValue = JSON::Any(SSC::replace(value, "\\\\", "\\\\")).str(); + const auto jsonValue = JSON::Any(replace(value, "\\\\", "\\\\")).str(); return createJavaScript("emit-to-render-process.js", "const name = decodeURIComponent(`" + event + "`); \n" diff --git a/src/runtime/json.hh b/src/runtime/json.hh new file mode 100644 index 0000000000..08eebb5ef4 --- /dev/null +++ b/src/runtime/json.hh @@ -0,0 +1,392 @@ +#ifndef SOCKET_RUNTIME_RUNTIME_JSON_H +#define SOCKET_RUNTIME_RUNTIME_JSON_H + +#include "platform.hh" + +namespace ssc::runtime::JSON { + // forward + class Any; + class Raw; + class Null; + class Object; + class Array; + class Boolean; + class Number; + class String; + + using ObjectEntries = Map; + using ArrayEntries = Vector; + + enum class Type { + Empty = -1, + Any = 0, + Null = 1, + Object = 2, + Array = 3, + Boolean = 4, + Number = 5, + String = 6, + Raw = 7, + Error = 8 + }; + + class Entity { + public: + using ID = uint64_t; + + ID id = rand64(); + + virtual ~Entity() = 0; + + operator bool () const; + + const runtime::String typeof () const; + bool isError () const; + bool isRaw () const; + bool isArray () const; + bool isBoolean () const; + bool isNumber () const; + bool isNull () const; + bool isObject () const; + bool isString () const; + bool isEmpty () const; + + const ID getEntityID (); + virtual Type getEntityType () const = 0; + virtual bool getEntityBooleanValue () const = 0; + virtual const runtime::String str () const = 0; + }; + + class SharedEntityPointer { + public: + struct ControlBlock { + std::atomic_size_t size; + Entity* entity; + + ControlBlock (Entity*); + }; + + SharedEntityPointer (Entity* = nullptr); + SharedEntityPointer (const SharedEntityPointer&); + SharedEntityPointer (SharedEntityPointer&&); + ~SharedEntityPointer (); + + SharedEntityPointer& operator = (const SharedEntityPointer&); + SharedEntityPointer& operator = (SharedEntityPointer&&); + Entity* operator -> () const; + operator bool () const; + + void reset (Entity* = nullptr); + size_t use_count () const; + void swap (SharedEntityPointer&); + + Entity* get () const; + template T* as () const; + + protected: + SharedPointer control = nullptr; + }; + + template + SharedEntityPointer make_shared (Args... args) { + static_assert(std::is_base_of::value, "T must derive from Entity"); + return new T(args...); + } + + template class Value : public Entity { + public: + using DataType = D; + Type type = t; + DataType data; + + virtual const DataType value () const = 0; + + Type getEntityType () const override { + return this->type; + } + + bool getEntityBooleanValue () const override { + return Entity::getEntityBooleanValue(); + } + }; + + class Error : public std::invalid_argument, public Value { + public: + static Type valueType; + int code = 0; + runtime::String name; + runtime::String message; + runtime::String location; + + Error (); + Error (const runtime::String& message); + Error (const Error&); + Error (Error*); + Error ( + const runtime::String& name, + const runtime::String& message, + int code = 0 + ); + Error ( + const runtime::String& name, + const runtime::String& message, + const runtime::String& location + ); + + const runtime::String value () const override; + const char* what () const noexcept override; + const runtime::String str () const override; + }; + + class Null : public Value { + public: + static Type valueType; + Null () = default; + Null (std::nullptr_t); + const std::nullptr_t value () const override; + const runtime::String str () const override; + }; + + class Any : public Value { + public: + static Type valueType; + + Any (Type, const SharedEntityPointer&); + Any (std::nullptr_t); + Any (const Null); + + Any (bool); + Any (int64_t); + Any (uint64_t); + Any (uint32_t); + Any (int32_t); + Any (double); + #if SOCKET_RUNTIME_PLATFORM_APPLE + Any (size_t); + Any (ssize_t); + #elif !SOCKET_RUNTIME_PLATFORM_WINDOWS + Any (long long); + #endif + + Any (Atomic&); + Any (Atomic&); + Any (Atomic&); + Any (Atomic&); + Any (Atomic&); + Any (Atomic&); + #if SOCKET_RUNTIME_PLATFORM_APPLE + Any (Atomic&); + Any (Atomic&); + #elif !SOCKET_RUNTIME_PLATFORM_WINDOWS + Any (Atomic&); + #endif + + Any (const char); + Any (const char *); + + Any (const runtime::String&); + Any (const runtime::Path&); + Any (const runtime::Map&); + Any (const runtime::Map&); + Any (const runtime::Map&); + Any (const runtime::Map&); + Any (const runtime::Map&); + Any (const runtime::Map&); + Any (const runtime::Map&); + Any (const runtime::Map&); + Any (const runtime::Map&); + #if SOCKET_RUNTIME_PLATFORM_APPLE + Any (const runtime::Map&); + Any (const runtime::Map&); + #elif !SOCKET_RUNTIME_PLATFORM_WINDOWS + Any (const runtime::Map&); + #endif + + Any (const Boolean&); + Any (const Number&); + Any (const String&); + Any (const Object&); + Any (const Array&); + Any (const Raw&); + Any (const Error&); + #if SOCKET_RUNTIME_PLATFORM_APPLE + Any (const NSError*); + #elif SOCKET_RUNTIME_PLATFORM_LINUX + Any (const GError*); + #endif + + Any (const ArrayEntries&); + Any (const ObjectEntries&); + + Any (); + Any (Any&&); + Any (const Any& any); + ~Any (); + + Any& operator = (const Any&); + Any& operator = (Any&&); + + Any& operator [] (const char*); + Any& operator [] (const runtime::String&); + Any& operator [] (const unsigned int); + + bool operator == (const Any&) const; + bool operator != (const Any&) const; + + template T& as () const; + Any& at (const runtime::String&); + Any& at (const unsigned int); + const SharedEntityPointer value () const override; + const runtime::String str () const override; + }; + + class Raw : public Value { + public: + static Type valueType; + Raw () = default; + Raw (const Raw&); + Raw (Raw&&); + Raw (const Raw*); + Raw (const Any&); + Raw (const runtime::String&); + Raw& operator = (const Raw&); + Raw& operator = (Raw&&); + const runtime::String value () const override; + const runtime::String str () const override; + }; + + class Object : public Value { + public: + static Type valueType; + using Entries = ObjectEntries; + using const_iterator = Entries::const_iterator; + using iterator = Entries::iterator; + Object () = default; + #if SOCKET_RUNTIME_PLATFORM_LINUX + Object (JSCValue*); + #endif + Object (const Map& entries); + Object (const Object::Entries& entries); + Object (const runtime::Map&); + Object (const runtime::Map&); + Object (const runtime::Map&); + Object (const runtime::Map&); + Object (const runtime::Map&); + Object (const runtime::Map&); + Object (const runtime::Map&); + Object (const runtime::Map&); + #if SOCKET_RUNTIME_PLATFORM_APPLE + Object (const runtime::Map&); + Object (const runtime::Map&); + #elif !SOCKET_RUNTIME_PLATFORM_WINDOWS + Object (const runtime::Map&); + #endif + Object (const Object&); + Object (const Error&); + + Any& operator [] (const char*); + Any& operator [] (const runtime::String&); + + const runtime::String str () const override; + const Object::Entries value () const override; + const Any& get (const runtime::String&) const; + const Any& get (const runtime::String&); + Any& at (const runtime::String&); + void set (const runtime::String&, const Any&); + bool has (const runtime::String&) const; + bool contains (const runtime::String&) const; + Entries::size_type size () const; + const const_iterator begin () const noexcept; + const const_iterator end () const noexcept; + iterator begin () noexcept; + iterator end () noexcept; + }; + + class Array : public Value { + public: + static Type valueType; + using Entries = ArrayEntries; + using const_iterator = Entries::const_iterator; + using iterator = Entries::iterator; + Array () = default; + Array (const Array&); + Array (const Array::Entries&); + #if SOCKET_RUNTIME_PLATFORM_LINUX + Array (JSCValue*); + #endif + + Any& operator [] (const unsigned int); + + const runtime::String str () const override; + const Array::Entries value () const override; + bool has (const unsigned int) const; + Entries::size_type size () const; + const Any& get (const unsigned int) const; + Any& at (const unsigned int); + void set (const unsigned int, const Any&); + void push (Any); + const Any pop (); + const const_iterator begin () const noexcept; + const const_iterator end () const noexcept; + iterator begin () noexcept; + iterator end () noexcept; + }; + + class Boolean : public Value { + public: + static Type valueType; + Boolean () = default; + Boolean (const Boolean&); + Boolean (bool); + Boolean (int); + Boolean (int64_t); + Boolean (double); + Boolean (void*); + Boolean (const runtime::String&); + + const bool value () const override; + const runtime::String str () const override; + }; + + class Number : public Value { + public: + static Type valueType; + Number () = default; + Number (const Number&); + Number (double); + Number (char); + Number (int); + Number (int64_t); + Number (bool); + Number (const String&); + + const double value () const override; + const runtime::String str () const override; + }; + + class String : public Value { + public: + static Type valueType; + String () = default; + String (const String&); + String (const runtime::String&); + String (const char); + String (const char*); + String (const Any&); + String (const Number&); + String (const Boolean&); + String (const Error&); + + const runtime::String str () const override; + const runtime::String value () const override; + runtime::String::size_type size () const; + }; + + extern const Null null; + extern const Any nullAny; + + inline const auto typeof (const Any& any) { + return any.typeof(); + } +} +#endif diff --git a/src/runtime/json/any.cc b/src/runtime/json/any.cc new file mode 100644 index 0000000000..3157a41063 --- /dev/null +++ b/src/runtime/json/any.cc @@ -0,0 +1,369 @@ +#include "../json.hh" + +namespace ssc::runtime::JSON { + const Any nullAny = nullptr; + + Type Any::valueType = Type::Any; + + Any::Any () { + this->data = nullptr; + this->type = Type::Empty; + } + + Any::Any (const Any& any) { + this->type = any.type; + this->data = any.data; + } + + Any::Any (Any&& any) { + this->data = std::move(any.data); + this->type = any.type; + any.type = Type::Empty; + any.data = nullptr; + } + + Any::Any (Type type, const SharedEntityPointer& data) { + this->data = data; + this->type = type; + } + + Any::Any (const Null null) { + this->data = JSON::make_shared(); + this->type = Type::Null; + } + + Any::Any (std::nullptr_t) { + this->data = JSON::make_shared(); + this->type = Type::Null; + } + + Any::Any (const char* string) { + this->data = JSON::make_shared(string); + this->type = Type::String; + } + + Any::Any (const char string) { + this->data = JSON::make_shared(string); + this->type = Type::String; + } + + Any::Any (const runtime::Path& path) + : Any(path.string()) + {} + + Any::Any (const runtime::Map& map) { + this->data = JSON::make_shared(map); + this->type = Type::Object; + } + + Any::Any (const runtime::Map& map) { + this->data = JSON::make_shared(map); + this->type = Type::Object; + } + + Any::Any (const runtime::Map& map) { + this->data = JSON::make_shared(map); + this->type = Type::Object; + } + + Any::Any (const runtime::Map& map) { + this->data = JSON::make_shared(map); + this->type = Type::Object; + } + + Any::Any (const runtime::Map& map) { + this->data = JSON::make_shared(map); + this->type = Type::Object; + } + + Any::Any (const runtime::Map& map) { + this->data = JSON::make_shared(map); + this->type = Type::Object; + } + + Any::Any (const runtime::Map& map) { + this->data = JSON::make_shared(map); + this->type = Type::Object; + } + + Any::Any (const runtime::Map& map) { + this->data = JSON::make_shared(map); + this->type = Type::Object; + } + + Any::Any (const runtime::Map& map) { + this->data = JSON::make_shared(map); + this->type = Type::Object; + } + +#if SOCKET_RUNTIME_PLATFORM_APPLE + Any::Any (const runtime::Map& map) { + this->data = JSON::make_shared(map); + this->type = Type::Object; + } + + Any::Any (const runtime::Map& map) { + this->data = JSON::make_shared(map); + this->type = Type::Object; + } +#elif !SOCKET_RUNTIME_PLATFORM_WINDOWS + Any::Any (const runtime::Map& map) { + this->data = JSON::make_shared(map); + this->type = Type::Object; + } +#endif + + Any::Any (const runtime::String& string) { + this->data = JSON::make_shared(string); + this->type = Type::String; + } + + Any::Any (const String& string) { + this->data = JSON::make_shared(string); + this->type = Type::String; + } + + Any::Any (bool boolean) { + this->data = JSON::make_shared(boolean); + this->type = Type::Boolean; + } + + Any::Any (const Boolean& boolean) { + this->data = JSON::make_shared(boolean); + this->type = Type::Boolean; + } + + Any::Any (int32_t number) { + this->data = JSON::make_shared((double) number); + this->type = Type::Number; + } + + Any::Any (uint32_t number) { + this->data = JSON::make_shared((double) number); + this->type = Type::Number; + } + + Any::Any (int64_t number) { + this->data = JSON::make_shared((double) number); + this->type = Type::Number; + } + + Any::Any (uint64_t number) { + this->data = JSON::make_shared((double) number); + this->type = Type::Number; + } + + Any::Any (double number) { + this->data = JSON::make_shared((double) number); + this->type = Type::Number; + } + +#if SOCKET_RUNTIME_PLATFORM_APPLE + Any::Any (size_t number) { + this->data = JSON::make_shared((double) number); + this->type = Type::Number; + } + + Any::Any (ssize_t number) { + this->data = JSON::make_shared((double) number); + this->type = Type::Number; + } +#elif !SOCKET_RUNTIME_PLATFORM_WINDOWS + Any::Any (long long number) { + this->data = JSON::make_shared((double) number); + this->type = Type::Number; + } +#endif + + Any::Any (Atomic& boolean) : Any(boolean.load()) {} + Any::Any (Atomic& number) : Any(number.load()) {} + Any::Any (Atomic& number) : Any(number.load()) {} + Any::Any (Atomic& number) : Any(number.load()) {} + Any::Any (Atomic& number) : Any(number.load()) {} + Any::Any (Atomic& number) : Any(number.load()) {} + + Any::Any (const Number& number) { + this->data = JSON::make_shared(number); + this->type = Type::Number; + } + + Any::Any (const Object& object) { + this->data = JSON::make_shared(object); + this->type = Type::Object; + } + + Any::Any (const Object::Entries& entries) { + this->data = JSON::make_shared(entries); + this->type = Type::Object; + } + + Any::Any (const Array& array) { + this->data = JSON::make_shared(array); + this->type = Type::Array; + } + + Any::Any (const Array::Entries& entries) { + this->data = JSON::make_shared(entries); + this->type = Type::Array; + } + + Any::Any (const Raw& source) { + this->data = JSON::make_shared(source); + this->type = Type::Raw; + } + + Any::Any (const Error& error) { + this->data = JSON::make_shared(error); + this->type = Type::Error; + } + +#if SOCKET_RUNTIME_PLATFORM_APPLE + Any::Any (const NSError* error) { + this->type = Type::Error; + this->data = JSON::make_shared( + error.domain.UTF8String, + error.localizedDescription.UTF8String, + error.code + ); + } +#elif SOCKET_RUNTIME_PLATFORM_LINUX + Any::Any (const GError* error) { + this->type = Type::Error; + this->data = JSON::make_shared( + g_quark_to_string(error->domain), + error->message, + error->code + ); + } +#endif + + Any::~Any () {} + + Any& Any::operator = (const Any& any) { + if (this != &any) { + this->type = any.type; + this->data = any.data; + } + return *this; + } + + Any& Any::operator = (Any&& any) { + if (this != &any) { + this->type = any.type; + this->data = std::move(any.data); + any.type = Type::Empty; + any.data = nullptr; + } + return *this; + } + + Any& Any::operator [](const char* key) { + return this->operator[](runtime::String(key)); + } + + Any& Any::operator [](const runtime::String& key) { + if (this->type == Type::Object) { + return this->as()[key]; + } + throw Error("TypeError", "cannot use operator[] on non-object type", __PRETTY_FUNCTION__); + } + + Any& Any::operator [](const unsigned int index) { + if (this->type == Type::Array) { + return this->as()[index]; + } + throw Error("TypeError", "cannot use operator[] on non-array type", __PRETTY_FUNCTION__); + } + + bool Any::operator == (const Any& input) const { + if (this->isEmpty() && input.isEmpty()) { + return true; + } + + if (this->isNull() && input.isNull()) { + return true; + } + + if (this->isString() && input.isString()) { + return this->str() == input.str(); + } + + if (this->isNumber() && input.isNumber()) { + return this->as().value() == input.as().value(); + } + + if (this->isBoolean() && input.isBoolean()) { + return this->as().value() == input.as().value(); + } + + return this->data.get() == input.data.get(); + } + + bool Any::operator != (const Any& input) const { + if (this->isEmpty() && !input.isEmpty()) { + return true; + } + + if (this->isNull() && !input.isNull()) { + return true; + } + + if (this->isString() && input.isString()) { + return this->str() != input.str(); + } + + if (this->isNumber() && input.isNumber()) { + return this->as().value() != input.as().value(); + } + + if (this->isBoolean() && input.isBoolean()) { + return this->as().value() != input.as().value(); + } + + return this->data.get() != input.data.get(); + } + + template Any& Any:: as() const; + template Raw& Any::as () const; + template Null& Any::as () const; + template Object& Any::as () const; + template Array& Any::as () const; + template Boolean& Any::as () const; + template Number& Any::as () const; + template String& Any::as () const; + template Error& Any::as () const; + + template T& JSON::Any::as () const { + if (this->type != T::valueType) { + throw Error("TypeError", "Invalid cast in Any::as()"); + } + + const auto ptr = this->data.get(); + + if (ptr != nullptr && this->type != Type::Null) { + return *static_cast(ptr); + } + + throw Error("BadCastError", "cannot cast to null value", __PRETTY_FUNCTION__); + } + + Any& Any::at (const runtime::String& key) { + return this->as().at(key); + } + + Any& Any::at (const unsigned int index) { + return this->as().at(index); + } + + const SharedEntityPointer Any::value () const { + return this->data; + } + + const runtime::String Any::str () const { + if (this->data) { + return this->data->str(); + } + + return ""; + } +} diff --git a/src/core/json/array.cc b/src/runtime/json/array.cc similarity index 78% rename from src/core/json/array.cc rename to src/runtime/json/array.cc index 5be15b62d5..6a50af4e33 100644 --- a/src/core/json/array.cc +++ b/src/runtime/json/array.cc @@ -1,6 +1,8 @@ #include "../json.hh" -namespace SSC::JSON { +namespace ssc::runtime::JSON { + Type Array::valueType = Type::Array; + Array::Array (const Array& array) { this->data = array.value(); } @@ -37,24 +39,24 @@ namespace SSC::JSON { } #endif - const SSC::String Array::str () const { - SSC::StringStream stream; + const runtime::String Array::str () const { + runtime::StringStream stream; auto count = this->data.size(); - stream << SSC::String("["); + stream << runtime::String("["); for (const auto& value : this->data) { stream << value.str(); if (--count > 0) { - stream << SSC::String(","); + stream << runtime::String(","); } } - stream << SSC::String("]"); + stream << runtime::String("]"); return stream.str(); } - Array::Entries Array::value () const { + const Array::Entries Array::value () const { return this->data; } @@ -66,12 +68,16 @@ namespace SSC::JSON { return this->data.size(); } - Any Array::get (const unsigned int index) const { + const Any& Array::get (const unsigned int index) const { if (index < this->data.size()) { return this->data.at(index); } - return nullptr; + return nullAny; + } + + Any& Array::at (const unsigned int index) { + return this->data.at(index); } void Array::set (const unsigned int index, const Any& value) { @@ -86,24 +92,16 @@ namespace SSC::JSON { this->set(this->size(), value); } - Any& Array::pop () { + const Any Array::pop () { if (this->size() == 0) { - return anyNull; + return nullAny; } - auto& value = this->data.back(); + auto value = this->data.back(); this->data.pop_back(); return value; } - Any Array::operator [] (const unsigned int index) const { - if (index >= this->data.size()) { - return nullptr; - } - - return this->data.at(index); - } - Any& Array::operator [] (const unsigned int index) { if (index >= this->data.size()) { this->data.resize(index + 1); @@ -119,4 +117,12 @@ namespace SSC::JSON { const Array::const_iterator Array::end () const noexcept { return this->data.end(); } + + Array::iterator Array::begin () noexcept { + return this->data.begin(); + } + + Array::iterator Array::end () noexcept { + return this->data.end(); + } } diff --git a/src/core/json/boolean.cc b/src/runtime/json/boolean.cc similarity index 72% rename from src/core/json/boolean.cc rename to src/runtime/json/boolean.cc index e8fba677f5..784a61f645 100644 --- a/src/core/json/boolean.cc +++ b/src/runtime/json/boolean.cc @@ -1,6 +1,8 @@ #include "../json.hh" -namespace SSC::JSON { +namespace ssc::runtime::JSON { + Type Boolean::valueType = Type::Boolean; + Boolean::Boolean (const Boolean& boolean) { this->data = boolean.value(); } @@ -25,15 +27,15 @@ namespace SSC::JSON { this->data = data != nullptr; } - Boolean:: Boolean (const SSC::String& string) { + Boolean:: Boolean (const runtime::String& string) { this->data = string.size() > 0; } - bool Boolean::value () const { + const bool Boolean::value () const { return this->data; } - const SSC::String Boolean::str () const { + const runtime::String Boolean::str () const { return this->data ? "true" : "false"; } } diff --git a/src/runtime/json/entity.cc b/src/runtime/json/entity.cc new file mode 100644 index 0000000000..a708f859c5 --- /dev/null +++ b/src/runtime/json/entity.cc @@ -0,0 +1,182 @@ +#include "../debug.hh" +#include "../json.hh" + +namespace ssc::runtime::JSON { + Entity::~Entity () {} + + bool Entity::getEntityBooleanValue () const { + if (this->isBoolean()) { + return dynamic_cast(this)->value(); + } else if (this->isNull() || this->isEmpty()) { + return false; + } else if (this->isNumber()) { + return dynamic_cast(this)->value() != 0; + } else if (this->isString()) { + return dynamic_cast(this)->size() > 0; + } else if (this->isObject()) { + return dynamic_cast(this)->size() > 0; + } else if (this->isArray()) { + return dynamic_cast(this)->size() > 0; + } else if (this->isRaw()) { + return dynamic_cast(this)->data.size() > 0; + } else if (this->getEntityType() == Type::Any) { + return dynamic_cast(this) + ->value() + ->getEntityBooleanValue(); + } else if (this->isError()) { + return true; + } + + return false; + } + + const runtime::String Entity::typeof () const { + static const char* table[] = { + "empty", + "raw", + "any", + "array", + "boolean", + "number", + "null", + "object", + "string", + "error" + }; + const auto type = this->getEntityType(); + return table[static_cast(type)]; + } + + bool Entity::isError () const { + return this->getEntityType() == Type::Error; + } + + bool Entity::isRaw () const { + return this->getEntityType() == Type::Raw; + } + + bool Entity::isArray () const { + return this->getEntityType() == Type::Array; + } + + bool Entity::isBoolean () const { + return this->getEntityType() == Type::Boolean; + } + + bool Entity::isNumber () const { + return this->getEntityType() == Type::Number; + } + + bool Entity::isNull () const { + return this->getEntityType() == Type::Null; + } + + bool Entity::isObject () const { + return this->getEntityType() == Type::Object; + } + + bool Entity::isString () const { + return this->getEntityType() == Type::String; + } + + bool Entity::isEmpty () const { + return this->getEntityType() == Type::Empty; + } + + const runtime::String Entity::str () const { + return ""; + } + + const Entity::ID Entity::getEntityID () { + return this->id; + } + + Entity::operator bool () const { + return this->getEntityBooleanValue(); + } + + SharedEntityPointer::ControlBlock::ControlBlock ( + Entity* entity + ) : entity(entity), + size(1) + {} + + SharedEntityPointer::SharedEntityPointer (Entity* entity) { + this->reset(entity); + } + + SharedEntityPointer::SharedEntityPointer (const SharedEntityPointer& other) + : control(other.control) + { + if (this->control) { + this->control->size.fetch_add(1, std::memory_order_relaxed); + } + } + + SharedEntityPointer::SharedEntityPointer (SharedEntityPointer&& other) + : control(std::move(other.control)) + { + other.control = nullptr; + } + + SharedEntityPointer::~SharedEntityPointer () { + this->reset(); + } + + SharedEntityPointer& SharedEntityPointer::operator = (const SharedEntityPointer& other) { + this->control = other.control; + if (this->control) { + this->control->size.fetch_add(1, std::memory_order_relaxed); + } + return *this; + } + + SharedEntityPointer& SharedEntityPointer::operator = (SharedEntityPointer&& other) { + this->control = std::move(other.control); + other.control = nullptr; + return *this; + } + + SharedEntityPointer::operator bool () const { + return this->get() != nullptr; + } + + Entity* SharedEntityPointer::operator -> () const { + return this->get(); + } + + void SharedEntityPointer::reset (Entity* entity) { + if (this->control && this->control->size.fetch_sub(1, std::memory_order_acq_rel) == 1) { + if (entity == this->control->entity) { + return; + } + + delete this->control->entity; + } + + if (entity) { + this->control = std::make_shared(entity); + } else { + this->control = nullptr; + } + } + + size_t SharedEntityPointer::use_count () const { + if (this->control) { + return this->control->size.load(); + } + return 0; + } + + void SharedEntityPointer::swap (SharedEntityPointer& other) { + std::swap(this->control, other.control); + } + + Entity* SharedEntityPointer::get () const { + return this->control ? this->control->entity : nullptr; + } + + template T* SharedEntityPointer::as () const { + return dynamic_cast(this->get()); + } +} diff --git a/src/core/json/error.cc b/src/runtime/json/error.cc similarity index 82% rename from src/core/json/error.cc rename to src/runtime/json/error.cc index 41832ac995..ac775f4385 100644 --- a/src/core/json/error.cc +++ b/src/runtime/json/error.cc @@ -1,6 +1,8 @@ #include "../json.hh" -namespace SSC::JSON { +namespace ssc::runtime::JSON { + Type Error::valueType = Type::Error; + Error::Error () : std::invalid_argument("") {} @@ -24,8 +26,8 @@ namespace SSC::JSON { } Error::Error ( - const SSC::String& name, - const SSC::String& message, + const runtime::String& name, + const runtime::String& message, int code ) : std::invalid_argument(name + ": " + message) { @@ -34,16 +36,16 @@ namespace SSC::JSON { this->message = message; } - Error::Error (const SSC::String& message) + Error::Error (const runtime::String& message) : std::invalid_argument(message) { this->message = message; } Error::Error ( - const SSC::String& name, - const SSC::String& message, - const SSC::String& location + const runtime::String& name, + const runtime::String& message, + const runtime::String& location ) : std::invalid_argument( name + ": " + message + " (from " + location + ")" ) @@ -53,7 +55,7 @@ namespace SSC::JSON { this->location = location; } - const SSC::String Error::value () const { + const runtime::String Error::value () const { return this->str(); } @@ -61,7 +63,7 @@ namespace SSC::JSON { return this->message.c_str(); } - const SSC::String Error::str () const { + const runtime::String Error::str () const { if (this->name.size() > 0 && this->message.size() > 0 && this->location.size() > 0) { return this->name + ": " + this->message + " (from " + this->location + ")"; } else if (this->name.size() > 0 && this->message.size() > 0) { diff --git a/src/runtime/json/null.cc b/src/runtime/json/null.cc new file mode 100644 index 0000000000..8f14dc3107 --- /dev/null +++ b/src/runtime/json/null.cc @@ -0,0 +1,19 @@ +#include "../json.hh" + +namespace ssc::runtime::JSON { + const Null null = nullptr; + + Type Null::valueType = Type::Null; + + Null::Null (std::nullptr_t) + : Null() + {} + + const std::nullptr_t Null::value () const { + return nullptr; + } + + const runtime::String Null::str () const { + return "null"; + } +} diff --git a/src/core/json/number.cc b/src/runtime/json/number.cc similarity index 80% rename from src/core/json/number.cc rename to src/runtime/json/number.cc index 0f54df156e..01ffaab3b5 100644 --- a/src/core/json/number.cc +++ b/src/runtime/json/number.cc @@ -1,8 +1,11 @@ #include "../json.hh" +#include "../debug.hh" + +namespace ssc::runtime::JSON { + Type Number::valueType = Type::Number; -namespace SSC::JSON { Number::Number (const String& string) { - this->data = std::stod(string.str()); + this->data = std::stod(string.data); } Number::Number (const Number& number) { @@ -29,11 +32,11 @@ namespace SSC::JSON { this->data = (double) number; } - float Number::value () const { + const double Number::value () const { return this->data; } - const SSC::String Number::str () const { + const runtime::String Number::str () const { if (this->data == 0) { return "0"; } diff --git a/src/runtime/json/object.cc b/src/runtime/json/object.cc new file mode 100644 index 0000000000..970ae7358c --- /dev/null +++ b/src/runtime/json/object.cc @@ -0,0 +1,256 @@ +#include "../string.hh" + +#include "../json.hh" + +using ssc::runtime::string::replace; + +namespace ssc::runtime::JSON { + Type Object::valueType = Type::Object; + +#if SOCKET_RUNTIME_PLATFORM_LINUX + Object::Object (JSCValue* value) { + if (value != nullptr && jsc_value_is_object(value) && !jsc_value_is_array(value)) { + const auto keys = jsc_value_object_enumerate_properties(value); + if (keys != nullptr) { + for (int i = 0; keys[i] != nullptr; ++i) { + const auto key = keys[i]; + const auto property = jsc_value_object_get_property(value, key); + if (jsc_value_is_string(property)) { + this->set(key, JSON::String(jsc_value_to_string(property))); + } else if (jsc_value_is_boolean(property)) { + this->set(key, JSON::Boolean(jsc_value_to_boolean(property))); + } else if (jsc_value_is_null(property)) { + this->set(key, JSON::Null()); + } else if (jsc_value_is_number(property)) { + this->set(key, JSON::Number(jsc_value_to_double(property))); + } else if (jsc_value_is_array(property)) { + this->set(key, JSON::Array(property)); + } else if (jsc_value_is_object(property)) { + this->set(key, JSON::Object(property)); + } + } + + g_strfreev(keys); + } + } + } +#endif + + Object::Object (const Object::Entries& entries) { + for (const auto& tuple : entries) { + const auto& key = tuple.first; + const auto& value = tuple.second; + this->data.insert_or_assign(key, value); + } + } + + Object::Object (const runtime::Map& map) { + for (const auto& tuple : map) { + const auto& key = tuple.first; + const auto value = Any(tuple.second); + this->data.insert_or_assign(key, value); + } + } + + Object::Object (const runtime::Map& map) { + for (const auto& tuple : map) { + const auto& key = tuple.first; + const auto value = Any(tuple.second); + this->data.insert_or_assign(key, value); + } + } + + Object::Object (const runtime::Map& map) { + for (const auto& tuple : map) { + const auto& key = tuple.first; + const auto value = Any(tuple.second); + this->data.insert_or_assign(key, value); + } + } + + Object::Object (const runtime::Map& map) { + for (const auto& tuple : map) { + const auto& key = tuple.first; + const auto value = Any(tuple.second); + this->data.insert_or_assign(key, value); + } + } + + Object::Object (const runtime::Map& map) { + for (const auto& tuple : map) { + const auto& key = tuple.first; + const auto value = Any(tuple.second); + this->data.insert_or_assign(key, value); + } + } + + Object::Object (const runtime::Map& map) { + for (const auto& tuple : map) { + const auto& key = tuple.first; + const auto value = Any(tuple.second); + this->data.insert_or_assign(key, value); + } + } + + Object::Object (const runtime::Map& map) { + for (const auto& tuple : map) { + const auto& key = tuple.first; + const auto value = Any(tuple.second); + this->data.insert_or_assign(key, value); + } + } + + Object::Object (const runtime::Map& map) { + for (const auto& tuple : map) { + const auto& key = tuple.first; + const auto value = Any(tuple.second); + this->data.insert_or_assign(key, value); + } + } + +#if SOCKET_RUNTIME_PLATFORM_APPLE + Object::Object (const runtime::Map& map) { + for (const auto& tuple : map) { + const auto& key = tuple.first; + const auto value = Any(tuple.second); + this->data.insert_or_assign(key, value); + } + } + + Object::Object (const runtime::Map& map) { + for (const auto& tuple : map) { + const auto& key = tuple.first; + const auto value = Any(tuple.second); + this->data.insert_or_assign(key, value); + } + } + +#elif !SOCKET_RUNTIME_PLATFORM_WINDOWS + Object::Object (const runtime::Map& map) { + for (const auto& tuple : map) { + const auto& key = tuple.first; + const auto value = Any(tuple.second); + this->data.insert_or_assign(key, value); + } + } +#endif + + Object::Object (const Object& object) { + this->data = object.value(); + } + + Object::Object (const Map& map) { + for (const auto& tuple : map) { + const auto& key = tuple.first; + const auto value = Any(tuple.second); + this->data.insert_or_assign(key, value); + } + } + + Object::Object (const Error& error) { + if (error.name.size() > 0) { + this->set("name", error.name); + } + + if (error.message.size() > 0) { + this->set("message", error.message); + } + + if (error.location.size() > 0) { + this->set("location", error.location); + } + + if (error.code != 0) { + this->set("code", error.code); + } + } + + const runtime::String Object::str () const { + runtime::StringStream stream; + auto count = this->data.size(); + stream << runtime::String("{"); + for (const auto& tuple : this->data) { + const auto key = replace(tuple.first, "\"","\\\""); + const auto value = tuple.second.str(); + + stream << runtime::String("\""); + stream << key; + stream << runtime::String("\":"); + stream << value; + + if (--count > 0) { + stream << runtime::String(","); + } + } + + stream << runtime::String("}"); + return stream.str(); + } + + const Object::Entries Object::value () const { + return this->data; + } + + const Any& Object::get (const runtime::String& key) const { + if (this->data.find(key) != this->data.end()) { + return this->data.at(key); + } + + return nullAny; + } + + const Any& Object::get (const runtime::String& key) { + if (this->data.find(key) != this->data.end()) { + return this->data.at(key); + } + + return nullAny; + } + + Any& Object::at (const runtime::String& key) { + return this->data.at(key); + } + + void Object::set (const runtime::String& key, const Any& value) { + this->data[key] = value; + } + + bool Object::has (const runtime::String& key) const { + return this->contains(key); + } + + bool Object::contains (const runtime::String& key) const { + return this->data.contains(key); + } + + Any& Object::operator [] (const char* key) { + return this->operator[](runtime::String(key)); + } + + Any& Object::operator [] (const runtime::String& key) { + if (!this->contains(key)) { + this->data[key] = Any {}; + } + return this->data.at(key); + } + + ObjectEntries::size_type Object::size () const { + return this->data.size(); + } + + const Object::const_iterator Object::begin () const noexcept { + return this->data.begin(); + } + + const Object::const_iterator Object::end () const noexcept { + return this->data.end(); + } + + Object::iterator Object::begin () noexcept { + return this->data.begin(); + } + + Object::iterator Object::end () noexcept { + return this->data.end(); + } +} diff --git a/src/runtime/json/raw.cc b/src/runtime/json/raw.cc new file mode 100644 index 0000000000..072aa0bc9a --- /dev/null +++ b/src/runtime/json/raw.cc @@ -0,0 +1,51 @@ +#include "../json.hh" + +namespace ssc::runtime::JSON { + Type Raw::valueType = Type::Raw; + + Raw::Raw (const Raw& raw) { + this->data = raw.data; + } + + Raw::Raw (Raw&& raw) { + this->data = std::move(raw.data); + raw.data = ""; + } + + Raw::Raw (const Raw* raw) { + if (raw != nullptr) { + this->data = raw->data; + } + } + + Raw::Raw (const runtime::String& source) { + this->data = source; + } + + Raw::Raw (const Any& source) { + if (source.isString()) { + this->data = source.data; + } else { + this->data = source.str(); + } + } + + Raw& Raw::operator = (const Raw& raw) { + this->data = raw.data; + return *this; + } + + Raw& Raw::operator = (Raw&& raw) { + this->data = std::move(raw.data); + raw.data = ""; + return *this; + } + + const runtime::String Raw::value () const { + return this->data; + } + + const runtime::String Raw::str () const { + return this->data; + } +} diff --git a/src/core/json/string.cc b/src/runtime/json/string.cc similarity index 60% rename from src/core/json/string.cc rename to src/runtime/json/string.cc index 80c985c123..2e0d7d703a 100644 --- a/src/core/json/string.cc +++ b/src/runtime/json/string.cc @@ -1,24 +1,30 @@ +#include "../string.hh" + #include "../json.hh" -namespace SSC::JSON { +using ssc::runtime::string::replace; + +namespace ssc::runtime::JSON { + Type String::valueType = Type::String; + String::String (const Number& number) { this->data = number.str(); } String::String (const String& data) { - this->data = SSC::String(data.value()); + this->data = runtime::String(data.value()); } - String::String (const SSC::String& data) { + String::String (const runtime::String& data) { this->data = data; } String::String (const char data) { - this->data = SSC::String(1, data); + this->data = runtime::String(1, data); } String::String (const char *data) { - this->data = SSC::String(data); + this->data = runtime::String(data); } String::String (const Any& any) { @@ -33,17 +39,17 @@ namespace SSC::JSON { this->data = error.str(); } - const SSC::String String::str () const { + const runtime::String String::str () const { auto escaped = replace(this->data, "\"", "\\\""); escaped = replace(escaped, "\\n", "\\\\n"); return "\"" + replace(escaped, "\n", "\\n") + "\""; } - SSC::String String::value () const { + const runtime::String String::value () const { return this->data; } - SSC::String::size_type String::size () const { + runtime::String::size_type String::size () const { return this->data.size(); } } diff --git a/src/runtime/loop.hh b/src/runtime/loop.hh new file mode 100644 index 0000000000..39a3552866 --- /dev/null +++ b/src/runtime/loop.hh @@ -0,0 +1,114 @@ +#ifndef SOCKET_RUNTIME_LOOP_H +#define SOCKET_RUNTIME_LOOP_H + +#include "platform.hh" +#include "options.hh" + +namespace ssc::runtime::loop { + class Loop { + public: + using DispatchCallback = Function; + + /** + * A container for libuv primatives. + */ + struct UV { + using RunMode = uv_run_mode; + + uv_loop_t loop; + uv_async_t async; + Atomic isInitialized = false; + + void open (Loop*); + void close (); + void stop (); + bool run (uv_run_mode = UV_RUN_DEFAULT); + int timeout () const; + int timeout (); + }; + + /** + * Various states the event loop will be in. + * even numbers are "final" states, odds are "pending" + */ + enum class State { + None = -1, + Idle = 0, + Stoping = 1, + Stopped = 2, + Pausing = 3, + Paused = 4, + Resuming = 5, + Resumed = 6, + Starting = 7, + Started = 8, + Polling = 9, + Shutdown = 20 + }; + + /** + * Various options to configure the event loop + */ + struct Options : public ssc::runtime::Options { + #if SOCKET_RUNTIME_PLATFORM_ANDROID || SOCKET_RUNTIME_PLATFORM_WINDOWS || SOCKET_RUNTIME_DESKTOP_EXTENSION + bool dedicatedThread = true; + #else + bool dedicatedThread = false; + #endif + }; + + #if SOCKET_RUNTIME_PLATFORM_LINUX + struct GTK { + GSource* source = nullptr; + GSourceFuncs functions; + }; + + GTK gtk; + #endif + + #if SOCKET_RUNTIME_PLATFORM_APPLE + dispatch_queue_t dispatchQueue = dispatch_queue_create( + "socket.runtime.core.loop.queue", + dispatch_queue_attr_make_with_qos_class( + DISPATCH_QUEUE_SERIAL, + QOS_CLASS_DEFAULT, + -1 + ) + ); + #endif + + const Options options; + Queue queue; + Thread thread; + Mutex mutex; + Atomic state = State::None; + UV uv; + + Loop () = default; + Loop (const Options&); + Loop (const Loop&) = delete; + Loop (Loop&&) = delete; + + Loop& operator = (const Loop&) = delete; + Loop& operator = (Loop&&) = delete; + + bool init (); + bool shutdown (); + bool start (); + bool stop (); + bool resume (); + bool pause (); + bool dispatch (const DispatchCallback&); + bool run (); + + uv_loop_t* get (); + bool alive () const; + bool started () const; + bool stopped () const; + bool paused () const; + int timeout (); + int timeout () const; + bool sleep (int64_t ms); + }; +} +#endif diff --git a/src/runtime/loop/loop.cc b/src/runtime/loop/loop.cc new file mode 100644 index 0000000000..f567571418 --- /dev/null +++ b/src/runtime/loop/loop.cc @@ -0,0 +1,317 @@ +#include "../debug.hh" +#include "../loop.hh" + +namespace ssc::runtime::loop { + // in milliseconds + constexpr int EVENT_LOOP_POLL_TIMEOUT = 32; + +#if SOCKET_RUNTIME_PLATFORM_LINUX + struct UVSource { + GSource base; // should ALWAYS be first member + gpointer tag; + Loop* loop = nullptr; + }; +#endif + + static void onAsync (uv_async_t* async) { + auto loop = reinterpret_cast(async->data); + + while (true) { + Loop::DispatchCallback callback = nullptr; + + do { + Lock lock(loop->mutex); + + if (loop->queue.size() > 0) { + callback = std::move(loop->queue.front()); + loop->queue.pop(); + } + } while (0); + + if (callback == nullptr) { + break; + } + + callback(); + } + } + + static void onLoopThread (Loop* loop) { + loop->state = Loop::State::Polling; + + while (loop->started() && loop->alive()) { + if (!loop->uv.run(UV_RUN_DEFAULT)) { + break; + } + } + + loop->state = Loop::State::Idle; + } + + void Loop::UV::open (Loop* loop) { + this->close(); + uv_loop_init(&this->loop); + uv_loop_set_data(&this->loop, reinterpret_cast(loop)); + uv_async_init(&this->loop, &this->async, onAsync); + this->async.data = reinterpret_cast(loop); + + this->isInitialized = true; + } + + void Loop::UV::close () { + if (this->isInitialized) { + uv_stop(&this->loop); + uv_run(&this->loop, UV_RUN_NOWAIT); + auto handle = reinterpret_cast(&async); + uv_close(handle, nullptr); + uv_run(&this->loop, UV_RUN_NOWAIT); + uv_loop_close(&this->loop); + } + + this->isInitialized = false; + } + + void Loop::UV::stop () { + if (this->isInitialized) { + uv_stop(&this->loop); + uv_run(&this->loop, UV_RUN_NOWAIT); + } + } + + int Loop::UV::timeout () { + uv_update_time(&this->loop); + return uv_backend_timeout(&this->loop); + } + + int Loop::UV::timeout () const { + return uv_backend_timeout(&this->loop); + } + + bool Loop::UV::run (uv_run_mode mode) { + if (mode == UV_RUN_DEFAULT) { + if (uv_run(&this->loop, mode) != 0) { + return uv_run(&this->loop, UV_RUN_NOWAIT); + } + } + + if (mode == UV_RUN_NOWAIT || mode == UV_RUN_ONCE) { + return uv_run(&this->loop, mode) != -1; + } + + return false; + } + + Loop::Loop (const Options& options) + : options(options) + {} + + bool Loop::init () { + if (this->state < State::Idle) { + this->state = State::Idle; + this->uv.open(this); + + #if SOCKET_RUNTIME_PLATFORM_LINUX + if (this->gtk.source) { + const auto id = g_source_get_id(this->gtk.source); + if (id > 0) { + g_source_remove(id); + } + + g_object_unref(this->gtk.source); + this->gtk.source = nullptr; + } + + // @see https://api.gtkd.org/glib.c.types.GSourceFuncs.html + this->gtk.functions.prepare = [](GSource *source, gint *timeout) -> gboolean { + auto loop = reinterpret_cast(source)->loop; + + if (!loop->started()) { + return false; + } + + if (!loop->alive()) { + return true; + } + + *timeout = loop->timeout(); + return *timeout == 0; + }; + + this->gtk.functions.check = [](GSource* source) -> gboolean { + const auto loop = reinterpret_cast(source)->loop; + const auto tag = reinterpret_cast(source)->tag; + const auto timeout = loop->timeout(); + + if (timeout == 0) { + return true; + } + + const auto condition = g_source_query_unix_fd(source, tag); + return ( + ((condition & G_IO_IN) == G_IO_IN) || + ((condition & G_IO_OUT) == G_IO_OUT) + ); + }; + + this->gtk.functions.dispatch = []( + GSource *source, + GSourceFunc callback, + gpointer user_data + ) -> gboolean { + const auto loop = reinterpret_cast(source)->loop; + loop->run(); + return G_SOURCE_CONTINUE; + }; + #endif + return true; + } + + return false; + } + + bool Loop::shutdown () { + if (this->state > State::None && this->state < State::Shutdown) { + if (this->stop()) { + this->uv.close(); + this->state = State::Shutdown; + return true; + } + + debug("Loop::start(): '* -> Shutdown' failed"); + } + + return false; + } + + bool Loop::start () { + if (this->state == State::Idle) { + this->state = State::Starting; + if (this->run()) { + this->state = State::Started; + return true; + } + + debug("Loop::start(): 'Starting -> Started' failed"); + } + + return false; + } + + bool Loop::stop () { + if (this->state > State::Stopped && this->state < State::Shutdown) { + Lock lock(this->mutex); + this->state = State::Stoping; + this->uv.stop(); + if (this->thread.joinable()) { + this->thread.join(); + } + this->state = State::Stopped; + return true; + } + + return false; + } + + bool Loop::resume () { + if (this->state < State::Resuming && this->state > State::None) { + this->state = State::Resuming; + if (this->run()) { + this->state = State::Resumed; + return true; + } + + debug("Loop::resume(): 'Resuming -> Resumed' failed"); + } + + return false; + } + + bool Loop::pause () { + Lock lock(this->mutex); + this->state = State::Pausing; + this->uv.stop(); + + // clean up old thread if still running + if (this->thread.joinable()) { + this->thread.join(); + } + + this->state = State::Paused; + + return false; + } + + bool Loop::dispatch (const DispatchCallback& callback) { + if (this->state < State::Starting) { + return false; + } + + do { + Lock lock(this->mutex); + this->queue.push(callback); + } while (0); + + uv_async_send(&this->uv.async); + return true; + } + + bool Loop::run () { + if (this->state > State::Idle) { + return false; + } + + Lock lock(this->mutex); + + this->init(); + + #if SOCKET_RUNTIME_PLATFORM_APPLE + dispatch_async(this->dispatchQueue, ^{ + onLoopThread(this); + }); + #else + // clean up old thread if still running + if (this->thread.joinable()) { + this->uv.stop(); + this->thread.join(); + } + + this->thread = Thread(&onLoopThread, this); + #endif + + return true; + } + + uv_loop_t* Loop::get () { + return &this->uv.loop; + } + + bool Loop::alive () const { + return uv_loop_alive(&this->uv.loop); + } + + bool Loop::started () const { + return this->state > State::Paused && this->state < State::Shutdown; + } + + bool Loop::stopped () const { + return this->state <= State::Stopped && this->state > State::None; + } + + bool Loop::paused () const { + return this->state == State::Pausing && this->state == State::Paused; + } + + int Loop::timeout () { + return this->uv.timeout(); + } + + int Loop::timeout () const { + return this->uv.timeout(); + } + + bool Loop::sleep (int64_t ms) { + return this->dispatch([ms]() { + msleep(ms); + }); + } +} diff --git a/src/core/options.hh b/src/runtime/options.hh similarity index 61% rename from src/core/options.hh rename to src/runtime/options.hh index 37bf17ee8f..12bf623f56 100644 --- a/src/core/options.hh +++ b/src/runtime/options.hh @@ -1,9 +1,9 @@ -#ifndef SOCKET_RUNTIME_CORE_OPTIONS_H -#define SOCKET_RUNTIME_CORE_OPTIONS_H +#ifndef SOCKET_RUNTIME_OPTIONS_H +#define SOCKET_RUNTIME_OPTIONS_H -#include "../platform/types.hh" +#include "platform.hh" -namespace SSC { +namespace ssc::runtime { struct Options { template const T& as () const { static_assert(std::is_base_of::value); diff --git a/src/runtime/os.hh b/src/runtime/os.hh new file mode 100644 index 0000000000..92f26ac737 --- /dev/null +++ b/src/runtime/os.hh @@ -0,0 +1,9 @@ +#ifndef SOCKET_RUNTIME_OS_H +#define SOCKET_RUNTIME_OS_H + +#include "platform.hh" + +namespace ssc::runtime::os { + const Map& constants (); +} +#endif diff --git a/src/runtime/os/constants.cc b/src/runtime/os/constants.cc new file mode 100644 index 0000000000..c978fb5ce4 --- /dev/null +++ b/src/runtime/os/constants.cc @@ -0,0 +1,343 @@ +#include "../os.hh" + +namespace ssc::runtime::os { + #define CONSTANT(c) { #c, (c) }, + static const Map CONSTANTS = { + #if defined(E2BIG) + CONSTANT(E2BIG) + #endif + #if defined(EACCES) + CONSTANT(EACCES) + #endif + #if defined(EADDRINUSE) + CONSTANT(EADDRINUSE) + #endif + #if defined(EADDRNOTAVAIL) + CONSTANT(EADDRNOTAVAIL) + #endif + #if defined(EAFNOSUPPORT) + CONSTANT(EAFNOSUPPORT) + #endif + #if defined(EAGAIN) + CONSTANT(EAGAIN) + #endif + #if defined(EALREADY) + CONSTANT(EALREADY) + #endif + #if defined(EBADF) + CONSTANT(EBADF) + #endif + #if defined(EBADMSG) + CONSTANT(EBADMSG) + #endif + #if defined(EBUSY) + CONSTANT(EBUSY) + #endif + #if defined(ECANCELED) + CONSTANT(ECANCELED) + #endif + #if defined(ECHILD) + CONSTANT(ECHILD) + #endif + #if defined(ECONNABORTED) + CONSTANT(ECONNABORTED) + #endif + #if defined(ECONNREFUSED) + CONSTANT(ECONNREFUSED) + #endif + #if defined(ECONNRESET) + CONSTANT(ECONNRESET) + #endif + #if defined(EDEADLK) + CONSTANT(EDEADLK) + #endif + #if defined(EDESTADDRREQ) + CONSTANT(EDESTADDRREQ) + #endif + #if defined(EDOM) + CONSTANT(EDOM) + #endif + #if defined(EDQUOT) + CONSTANT(EDQUOT) + #endif + #if defined(EEXIST) + CONSTANT(EEXIST) + #endif + #if defined(EFAULT) + CONSTANT(EFAULT) + #endif + #if defined(EFBIG) + CONSTANT(EFBIG) + #endif + #if defined(EHOSTUNREACH) + CONSTANT(EHOSTUNREACH) + #endif + #if defined(EIDRM) + CONSTANT(EIDRM) + #endif + #if defined(EILSEQ) + CONSTANT(EILSEQ) + #endif + #if defined(EINPROGRESS) + CONSTANT(EINPROGRESS) + #endif + #if defined(EINTR) + CONSTANT(EINTR) + #endif + #if defined(EINVAL) + CONSTANT(EINVAL) + #endif + #if defined(EIO) + CONSTANT(EIO) + #endif + #if defined(EISCONN) + CONSTANT(EISCONN) + #endif + #if defined(EISDIR) + CONSTANT(EISDIR) + #endif + #if defined(ELOOP) + CONSTANT(ELOOP) + #endif + #if defined(EMFILE) + CONSTANT(EMFILE) + #endif + #if defined(EMLINK) + CONSTANT(EMLINK) + #endif + #if defined(EMSGSIZE) + CONSTANT(EMSGSIZE) + #endif + #if defined(EMULTIHOP) + CONSTANT(EMULTIHOP) + #endif + #if defined(ENAMETOOLONG) + CONSTANT(ENAMETOOLONG) + #endif + #if defined(ENETDOWN) + CONSTANT(ENETDOWN) + #endif + #if defined(ENETRESET) + CONSTANT(ENETRESET) + #endif + #if defined(ENETUNREACH) + CONSTANT(ENETUNREACH) + #endif + #if defined(ENFILE) + CONSTANT(ENFILE) + #endif + #if defined(ENOBUFS) + CONSTANT(ENOBUFS) + #endif + #if defined(ENODATA) + CONSTANT(ENODATA) + #endif + #if defined(ENODEV) + CONSTANT(ENODEV) + #endif + #if defined(ENOENT) + CONSTANT(ENOENT) + #endif + #if defined(ENOEXEC) + CONSTANT(ENOEXEC) + #endif + #if defined(ENOLCK) + CONSTANT(ENOLCK) + #endif + #if defined(ENOLINK) + CONSTANT(ENOLINK) + #endif + #if defined(ENOMEM) + CONSTANT(ENOMEM) + #endif + #if defined(ENOMSG) + CONSTANT(ENOMSG) + #endif + #if defined(ENOPROTOOPT) + CONSTANT(ENOPROTOOPT) + #endif + #if defined(ENOSPC) + CONSTANT(ENOSPC) + #endif + #if defined(ENOSR) + CONSTANT(ENOSR) + #endif + #if defined(ENOSTR) + CONSTANT(ENOSTR) + #endif + #if defined(ENOSYS) + CONSTANT(ENOSYS) + #endif + #if defined(ENOTCONN) + CONSTANT(ENOTCONN) + #endif + #if defined(ENOTDIR) + CONSTANT(ENOTDIR) + #endif + #if defined(ENOTEMPTY) + CONSTANT(ENOTEMPTY) + #endif + #if defined(ENOTSOCK) + CONSTANT(ENOTSOCK) + #endif + #if defined(ENOTSUP) + CONSTANT(ENOTSUP) + #endif + #if defined(ENOTTY) + CONSTANT(ENOTTY) + #endif + #if defined(ENXIO) + CONSTANT(ENXIO) + #endif + #if defined(EOPNOTSUPP) + CONSTANT(EOPNOTSUPP) + #endif + #if defined(EOVERFLOW) + CONSTANT(EOVERFLOW) + #endif + #if defined(EPERM) + CONSTANT(EPERM) + #endif + #if defined(EPIPE) + CONSTANT(EPIPE) + #endif + #if defined(EPROTO) + CONSTANT(EPROTO) + #endif + #if defined(EPROTONOSUPPORT) + CONSTANT(EPROTONOSUPPORT) + #endif + #if defined(EPROTOTYPE) + CONSTANT(EPROTOTYPE) + #endif + #if defined(ERANGE) + CONSTANT(ERANGE) + #endif + #if defined(EROFS) + CONSTANT(EROFS) + #endif + #if defined(ESPIPE) + CONSTANT(ESPIPE) + #endif + #if defined(ESRCH) + CONSTANT(ESRCH) + #endif + #if defined(ESTALE) + CONSTANT(ESTALE) + #endif + #if defined(ETIME) + CONSTANT(ETIME) + #endif + #if defined(ETIMEDOUT) + CONSTANT(ETIMEDOUT) + #endif + #if defined(ETXTBSY) + CONSTANT(ETXTBSY) + #endif + #if defined(EWOULDBLOCK) + CONSTANT(EWOULDBLOCK) + #endif + #if defined(EXDEV) + CONSTANT(EXDEV) + #endif + + #if defined(SIGHUP) + CONSTANT(SIGHUP) + #endif + #if defined(SIGINT) + CONSTANT(SIGINT) + #endif + #if defined(SIGQUIT) + CONSTANT(SIGQUIT) + #endif + #if defined(SIGILL) + CONSTANT(SIGILL) + #endif + #if defined(SIGTRAP) + CONSTANT(SIGTRAP) + #endif + #if defined(SIGABRT) + CONSTANT(SIGABRT) + #endif + #if defined(SIGIOT) + CONSTANT(SIGIOT) + #endif + #if defined(SIGBUS) + CONSTANT(SIGBUS) + #endif + #if defined(SIGFPE) + CONSTANT(SIGFPE) + #endif + #if defined(SIGKILL) + CONSTANT(SIGKILL) + #endif + #if defined(SIGUSR1) + CONSTANT(SIGUSR1) + #endif + #if defined(SIGSEGV) + CONSTANT(SIGSEGV) + #endif + #if defined(SIGUSR2) + CONSTANT(SIGUSR2) + #endif + #if defined(SIGPIPE) + CONSTANT(SIGPIPE) + #endif + #if defined(SIGALRM) + CONSTANT(SIGALRM) + #endif + #if defined(SIGTERM) + CONSTANT(SIGTERM) + #endif + #if defined(SIGCHLD) + CONSTANT(SIGCHLD) + #endif + #if defined(SIGCONT) + CONSTANT(SIGCONT) + #endif + #if defined(SIGSTOP) + CONSTANT(SIGSTOP) + #endif + #if defined(SIGTSTP) + CONSTANT(SIGTSTP) + #endif + #if defined(SIGTTIN) + CONSTANT(SIGTTIN) + #endif + #if defined(SIGTTOU) + CONSTANT(SIGTTOU) + #endif + #if defined(SIGURG) + CONSTANT(SIGURG) + #endif + #if defined(SIGXCPU) + CONSTANT(SIGXCPU) + #endif + #if defined(SIGXFSZ) + CONSTANT(SIGXFSZ) + #endif + #if defined(SIGVTALRM) + CONSTANT(SIGVTALRM) + #endif + #if defined(SIGPROF) + CONSTANT(SIGPROF) + #endif + #if defined(SIGWINCH) + CONSTANT(SIGWINCH) + #endif + #if defined(SIGIO) + CONSTANT(SIGIO) + #endif + #if defined(SIGINFO) + CONSTANT(SIGINFO) + #endif + #if defined(SIGSYS) + CONSTANT(SIGSYS) + #endif + }; + #undef CONSTANT + + const Map& constants () { + return CONSTANTS; + } +} diff --git a/src/runtime/platform.hh b/src/runtime/platform.hh new file mode 100644 index 0000000000..cc4b8f7a42 --- /dev/null +++ b/src/runtime/platform.hh @@ -0,0 +1,23 @@ +#ifndef SOCKET_RUNTIME_PLATFORM_H +#define SOCKET_RUNTIME_PLATFORM_H + +#include "platform/system.hh" + +namespace ssc::runtime { + struct RuntimePlatform { + const String arch; + const String os; + bool mac = false; + bool ios = false; + bool win = false; + bool android = false; + bool linux = false; + bool unix = false; + }; + + extern const RuntimePlatform platform; + void msleep (uint64_t ms); + uint64_t rand64 (); +} + +#endif diff --git a/src/platform/android.hh b/src/runtime/platform/android.hh similarity index 100% rename from src/platform/android.hh rename to src/runtime/platform/android.hh diff --git a/src/platform/android/content_resolver.cc b/src/runtime/platform/android/content_resolver.cc similarity index 65% rename from src/platform/android/content_resolver.cc rename to src/runtime/platform/android/content_resolver.cc index 3306234942..8c889b3f91 100644 --- a/src/platform/android/content_resolver.cc +++ b/src/runtime/platform/android/content_resolver.cc @@ -1,26 +1,56 @@ -#include "../../core/resource.hh" -#include "../../core/url.hh" +#include "../../filesystem.hh" +#include "../../url.hh" #include "content_resolver.hh" -namespace SSC::Android { +namespace ssc::runtime::android { + ContentResolver::ContentResolver (const ContentResolver& resolver) { + this->activity = resolver.activity; + this->jvm = resolver.jvm; + } + + ContentResolver::ContentResolver (ContentResolver&& resolver) { + this->activity = resolver.activity; + this->jvm = resolver.jvm; + resolver->jvm = nullptr; + resolver->activity = nullptr; + } + + ContentResolver& ContentResolver::operator = (const ContentResolver& resolver) { + this->activity = resolver.activity; + this->jvm = resolver.jvm; + return *this; + } + + ContentResolver& ContentResolver::operator = (ContentResolver&& resolver) { + this->activity = resolver.activity; + this->jvm = resolver.jvm; + resolver->jvm = nullptr; + resolver->activity = nullptr; + return *this; + } + bool ContentResolver::isDocumentURI (const String& uri) { - const auto attachment = JNIEnvironmentAttachment(this->jvm); - const auto platform = (jobject) CallObjectClassMethodFromAndroidEnvironment( - attachment.env, - this->activity, - "getAppPlatform", - "()Lsocket/runtime/app/AppPlatform;" - ); + if (this->jvm && this->activity) { + const auto attachment = JNIEnvironmentAttachment(this->jvm); + const auto platform = (jobject) CallObjectClassMethodFromAndroidEnvironment( + attachment.env, + this->activity, + "getAppPlatform", + "()Lsocket/runtime/app/AppPlatform;" + ); - return CallClassMethodFromAndroidEnvironment( - attachment.env, - Boolean, - platform, - "isDocumentURI", - "(Ljava/lang/String;)Z", - attachment.env->NewStringUTF(uri.c_str()) - ); + return CallClassMethodFromAndroidEnvironment( + attachment.env, + Boolean, + platform, + "isDocumentURI", + "(Ljava/lang/String;)Z", + attachment.env->NewStringUTF(uri.c_str()) + ); + } + + return false; } bool ContentResolver::isContentURI (const String& uri) { @@ -49,23 +79,27 @@ namespace SSC::Android { } String ContentResolver::getContentMimeType (const String& uri) { - const auto attachment = JNIEnvironmentAttachment(this->jvm); - const auto platform = (jobject) CallObjectClassMethodFromAndroidEnvironment( - attachment.env, - this->activity, - "getAppPlatform", - "()Lsocket/runtime/app/AppPlatform;" - ); + if (this->jvm && this->activity) { + const auto attachment = JNIEnvironmentAttachment(this->jvm); + const auto platform = (jobject) CallObjectClassMethodFromAndroidEnvironment( + attachment.env, + this->activity, + "getAppPlatform", + "()Lsocket/runtime/app/AppPlatform;" + ); - const auto contentMimeTypeString = (jstring) CallObjectClassMethodFromAndroidEnvironment( - attachment.env, - platform, - "getContentMimeType", - "(Ljava/lang/String;)Ljava/lang/String;", - attachment.env->NewStringUTF(uri.c_str()) - ); + const auto contentMimeTypeString = (jstring) CallObjectClassMethodFromAndroidEnvironment( + attachment.env, + platform, + "getContentMimeType", + "(Ljava/lang/String;)Ljava/lang/String;", + attachment.env->NewStringUTF(uri.c_str()) + ); + + return StringWrap(attachment.env, contentMimeTypeString).str(); + } - return StringWrap(attachment.env, contentMimeTypeString).str(); + return ""; } String ContentResolver::getPathnameFromURI (const String& uri) { @@ -75,7 +109,7 @@ namespace SSC::Android { return url.pathname; } - if (this->isDocumentURI(uri)) { + if (this->jvm && this->isDocumentURI(uri)) { const auto attachment = JNIEnvironmentAttachment(this->jvm); const auto platform = (jobject) CallObjectClassMethodFromAndroidEnvironment( attachment.env, @@ -95,7 +129,7 @@ namespace SSC::Android { const auto documentID = StringWrap(attachment.env, documentIDString).str(); if (this->isExternalStorageDocumentURI(uri)) { - const auto externalStorage = FileResource::getExternalAndroidStorageDirectory(); + const auto externalStorage = filesystem::Resource::getExternalAndroidStorageDirectory(); const auto parts = split(documentID, ":"); const auto type = parts[0]; @@ -153,50 +187,63 @@ namespace SSC::Android { const String& uri, const String& id ) { - const auto attachment = JNIEnvironmentAttachment(this->jvm); - const auto platform = (jobject) CallObjectClassMethodFromAndroidEnvironment( - attachment.env, - this->activity, - "getAppPlatform", - "()Lsocket/runtime/app/AppPlatform;" - ); + if (this->jvm && this->activity) { + const auto attachment = JNIEnvironmentAttachment(this->jvm); + const auto platform = (jobject) CallObjectClassMethodFromAndroidEnvironment( + attachment.env, + this->activity, + "getAppPlatform", + "()Lsocket/runtime/app/AppPlatform;" + ); - const auto result = (jstring) CallObjectClassMethodFromAndroidEnvironment( - attachment.env, - platform, - "getPathnameFromContentURIDataColumn", - "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", - attachment.env->NewStringUTF(uri.c_str()), - attachment.env->NewStringUTF(id.c_str()) - ); + const auto result = (jstring) CallObjectClassMethodFromAndroidEnvironment( + attachment.env, + platform, + "getPathnameFromContentURIDataColumn", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", + attachment.env->NewStringUTF(uri.c_str()), + attachment.env->NewStringUTF(id.c_str()) + ); - return StringWrap(attachment.env, result).str(); + return StringWrap(attachment.env, result).str(); + } + + return ""; } String ContentResolver::getExternalContentURIForType (const String& type) { - const auto attachment = JNIEnvironmentAttachment(this->jvm); - const auto platform = (jobject) CallObjectClassMethodFromAndroidEnvironment( - attachment.env, - this->activity, - "getAppPlatform", - "()Lsocket/runtime/app/AppPlatform;" - ); + if (this->jvm && this->activity) { + const auto attachment = JNIEnvironmentAttachment(this->jvm); + const auto platform = (jobject) CallObjectClassMethodFromAndroidEnvironment( + attachment.env, + this->activity, + "getAppPlatform", + "()Lsocket/runtime/app/AppPlatform;" + ); - const auto result = (jstring) CallObjectClassMethodFromAndroidEnvironment( - attachment.env, - platform, - "getExternalContentURIForType", - "(Ljava/lang/String;)Ljava/lang/String;", - attachment.env->NewStringUTF(type.c_str()) - ); + const auto result = (jstring) CallObjectClassMethodFromAndroidEnvironment( + attachment.env, + platform, + "getExternalContentURIForType", + "(Ljava/lang/String;)Ljava/lang/String;", + attachment.env->NewStringUTF(type.c_str()) + ); - return StringWrap(attachment.env, result).str(); + return StringWrap(attachment.env, result).str(); + } + + return ""; } Vector ContentResolver::getPathnameEntriesFromContentURI ( const String& uri ) { Vector entries; + + if (!this->jvm || !this->activity) { + return entries; + } + const auto attachment = JNIEnvironmentAttachment(this->jvm); const auto platform = (jobject) CallObjectClassMethodFromAndroidEnvironment( attachment.env, @@ -228,7 +275,7 @@ namespace SSC::Android { } bool ContentResolver::hasAccess (const String& uri) { - if (!uri.starts_with("content:") & !uri.starts_with("android.resource:")) { + if (!this->jvm || !this->activity || -!uri.starts_with("content:") & !uri.starts_with("android.resource:")) { return false; } @@ -255,7 +302,7 @@ namespace SSC::Android { off_t* offset, off_t* length ) { - if (!uri.starts_with("content:") & !uri.starts_with("android.resource:")) { + if (!this->jvm || !this->activity || !uri.starts_with("content:") & !uri.starts_with("android.resource:")) { return nullptr; } @@ -303,7 +350,7 @@ namespace SSC::Android { } bool ContentResolver::closeFileDescriptor (ContentResolver::FileDescriptor fileDescriptor) { - if (fileDescriptor == nullptr) { + if (!this->jvm || !this->activity || fileDescriptor == nullptr) { return false; } @@ -321,7 +368,7 @@ namespace SSC::Android { } size_t ContentResolver::getFileDescriptorLength (FileDescriptor fileDescriptor) { - if (fileDescriptor == nullptr) { + if (!this->jvm || !this->activity || fileDescriptor == nullptr) { return -EINVAL; } @@ -336,7 +383,7 @@ namespace SSC::Android { } size_t ContentResolver::getFileDescriptorOffset (FileDescriptor fileDescriptor) { - if (fileDescriptor == nullptr) { + if (!this->jvm || !this->activity || fileDescriptor == nullptr) { return -EINVAL; } @@ -351,7 +398,7 @@ namespace SSC::Android { } int ContentResolver::getFileDescriptorFD (FileDescriptor fileDescriptor) { - if (fileDescriptor == nullptr) { + if (!this->jvm || !this->activity || fileDescriptor == nullptr) { return -EINVAL; } diff --git a/src/platform/android/content_resolver.hh b/src/runtime/platform/android/content_resolver.hh similarity index 85% rename from src/platform/android/content_resolver.hh rename to src/runtime/platform/android/content_resolver.hh index 2e4dd8357e..11524388ea 100644 --- a/src/platform/android/content_resolver.hh +++ b/src/runtime/platform/android/content_resolver.hh @@ -4,7 +4,7 @@ #include "environment.hh" #include "types.hh" -namespace SSC::Android { +namespace ssc::runtime::android { class ContentResolver { public: using FileDescriptor = jobject; @@ -12,6 +12,11 @@ namespace SSC::Android { Activity activity; JVMEnvironment jvm; ContentResolver () = default; + ContentResolver (const ContentResolver&); + ContentResolver (ContentResolver&&); + + ContentResolver& operator = (const ContentResolver&); + ContentResolver& operator = (ContentResolver&&); bool isDocumentURI (const String& uri); bool isContentURI (const String& uri); diff --git a/src/platform/android/environment.cc b/src/runtime/platform/android/environment.cc similarity index 99% rename from src/platform/android/environment.cc rename to src/runtime/platform/android/environment.cc index 5e9078ed20..7e4998bae4 100644 --- a/src/platform/android/environment.cc +++ b/src/runtime/platform/android/environment.cc @@ -1,7 +1,7 @@ #include "environment.hh" #include "../../core/debug.hh" -namespace SSC::Android { +namespace ssc::android { JVMEnvironment::JVMEnvironment (JNIEnv* env) { this->jniVersion = env->GetVersion(); env->GetJavaVM(&jvm); diff --git a/src/platform/android/environment.hh b/src/runtime/platform/android/environment.hh similarity index 99% rename from src/platform/android/environment.hh rename to src/runtime/platform/android/environment.hh index 045ef17d12..040027dfe7 100644 --- a/src/platform/android/environment.hh +++ b/src/runtime/platform/android/environment.hh @@ -100,7 +100,7 @@ env->CallVoidMethod(object, ID, ##__VA_ARGS__); \ }) -namespace SSC::Android { +namespace ssc::android { /** * A `JVMEnvironment` constainer holders a poitner the current `JavaVM` * instance for the current `JNIEnv`. diff --git a/src/platform/android/looper.cc b/src/runtime/platform/android/looper.cc similarity index 93% rename from src/platform/android/looper.cc rename to src/runtime/platform/android/looper.cc index 18429e216c..49093a1e7d 100644 --- a/src/platform/android/looper.cc +++ b/src/runtime/platform/android/looper.cc @@ -1,12 +1,12 @@ #include "looper.hh" #include "../../core/debug.hh" -namespace SSC::Android { +namespace ssc::android { Looper::Looper (JVMEnvironment jvm) : jvm(jvm) {} - void Looper::dispatch (const DispatchCallback& callback) { + void Looper::dispatch (const DispatchCallback callback) { while (!this->isReady) { msleep(2); } @@ -16,7 +16,7 @@ namespace SSC::Android { write(this->fds[1], &context, sizeof(DispatchCallbackContext*)); } - bool Looper::acquire (const LoopCallback& callback) { + bool Looper::acquire (const LoopCallback callback) { if (pipe(this->fds) != 0) { return false; } diff --git a/src/platform/android/looper.hh b/src/runtime/platform/android/looper.hh similarity index 87% rename from src/platform/android/looper.hh rename to src/runtime/platform/android/looper.hh index a934427298..be5af6d862 100644 --- a/src/platform/android/looper.hh +++ b/src/runtime/platform/android/looper.hh @@ -5,7 +5,7 @@ #include "environment.hh" #include "native.hh" -namespace SSC::Android { +namespace ssc::android { struct Looper { using LoopCallback = Function; using DispatchCallback = Function; @@ -14,7 +14,7 @@ namespace SSC::Android { const Looper::LoopCallback callback; Looper* looper = nullptr; LoopCallbackContext ( - const Looper::LoopCallback& callback, + const Looper::LoopCallback callback, Looper* looper ) : callback(callback), looper(looper) @@ -25,7 +25,7 @@ namespace SSC::Android { const Looper::DispatchCallback callback; Looper* looper = nullptr; DispatchCallbackContext ( - const Looper::DispatchCallback& callback, + const Looper::DispatchCallback callback, Looper* looper ) : callback(callback), looper(looper) @@ -52,9 +52,9 @@ namespace SSC::Android { Looper operator= (const Looper&) = delete; Looper operator= (Looper&&) = delete; - bool acquire (const LoopCallback& callback = nullptr); + bool acquire (const LoopCallback callback = nullptr); void release (); - void dispatch (const DispatchCallback& callback); + void dispatch (const DispatchCallback callback); bool isAcquired () const; }; } diff --git a/src/platform/android/mime.cc b/src/runtime/platform/android/mime.cc similarity index 97% rename from src/platform/android/mime.cc rename to src/runtime/platform/android/mime.cc index cd627074ef..dc46117e30 100644 --- a/src/platform/android/mime.cc +++ b/src/runtime/platform/android/mime.cc @@ -1,7 +1,7 @@ #include "mime.hh" #include "string_wrap.hh" -namespace SSC::Android { +namespace ssc::android { static MimeTypeMap sharedMimeTypeMap; void initializeMimeTypeMap (MimeTypeMapRef ref, JVMEnvironment jvm) { diff --git a/src/platform/android/mime.hh b/src/runtime/platform/android/mime.hh similarity index 94% rename from src/platform/android/mime.hh rename to src/runtime/platform/android/mime.hh index c7cbe2a3d1..b411f4afc4 100644 --- a/src/platform/android/mime.hh +++ b/src/runtime/platform/android/mime.hh @@ -4,7 +4,7 @@ #include "../types.hh" #include "environment.hh" -namespace SSC::Android { +namespace ssc::android { using MimeTypeMapRef = jobject; struct MimeTypeMap { diff --git a/src/platform/android/native.hh b/src/runtime/platform/android/native.hh similarity index 100% rename from src/platform/android/native.hh rename to src/runtime/platform/android/native.hh diff --git a/src/platform/android/string_wrap.cc b/src/runtime/platform/android/string_wrap.cc similarity index 98% rename from src/platform/android/string_wrap.cc rename to src/runtime/platform/android/string_wrap.cc index d9070d0a99..4289c7406c 100644 --- a/src/platform/android/string_wrap.cc +++ b/src/runtime/platform/android/string_wrap.cc @@ -1,6 +1,6 @@ #include "string_wrap.hh" -namespace SSC::Android { +namespace ssc::android { StringWrap::StringWrap (JNIEnv *env) { this->env = env; this->ref = nullptr; diff --git a/src/platform/android/string_wrap.hh b/src/runtime/platform/android/string_wrap.hh similarity index 97% rename from src/platform/android/string_wrap.hh rename to src/runtime/platform/android/string_wrap.hh index dcff37dc24..9d2423173c 100644 --- a/src/platform/android/string_wrap.hh +++ b/src/runtime/platform/android/string_wrap.hh @@ -4,7 +4,7 @@ #include "../types.hh" #include "native.hh" -namespace SSC::Android { +namespace ssc::android { /** * A container for a JNI string (jstring). */ diff --git a/src/platform/android/types.hh b/src/runtime/platform/android/types.hh similarity index 97% rename from src/platform/android/types.hh rename to src/runtime/platform/android/types.hh index e24e0bb10a..ca31f1e7ba 100644 --- a/src/platform/android/types.hh +++ b/src/runtime/platform/android/types.hh @@ -4,7 +4,7 @@ #include "../types.hh" #include "native.hh" -namespace SSC::Android { +namespace ssc::android { /** * An Android AssetManager NDK type. */ diff --git a/src/platform/platform.cc b/src/runtime/platform/platform.cc similarity index 76% rename from src/platform/platform.cc rename to src/runtime/platform/platform.cc index b29135c759..5ac38c9c7d 100644 --- a/src/platform/platform.cc +++ b/src/runtime/platform/platform.cc @@ -1,9 +1,6 @@ -#include "platform.hh" +#include "../platform.hh" -#define IMAX_BITS(m) ((m)/((m) % 255+1) / 255 % 255 * 8 + 7-86 / ((m) % 255+12)) -#define RAND_MAX_WIDTH IMAX_BITS(RAND_MAX) - -namespace SSC { +namespace ssc::runtime { extern const RuntimePlatform platform = { #if defined(__x86_64__) || defined(_M_X64) .arch = "x86_64", @@ -73,23 +70,6 @@ namespace SSC { #endif }; - uint64_t rand64 () { - static const auto maxWidth = RAND_MAX_WIDTH; - static bool init = false; - - if (!init) { - init = true; - srand(time(0)); - } - - uint64_t r = 0; - for (int i = 0; i < 64; i += maxWidth) { - r <<= maxWidth; - r ^= (unsigned) rand(); - } - return r; - } - void msleep (uint64_t ms) { std::this_thread::yield(); std::this_thread::sleep_for(std::chrono::milliseconds(ms)); diff --git a/src/platform/platform.hh b/src/runtime/platform/system.hh similarity index 87% rename from src/platform/platform.hh rename to src/runtime/platform/system.hh index f09e395ac5..64e0546784 100644 --- a/src/platform/platform.hh +++ b/src/runtime/platform/system.hh @@ -1,5 +1,5 @@ -#ifndef SOCKET_RUNTIME_PLATFORM_PLATFORM_H -#define SOCKET_RUNTIME_PLATFORM_PLATFORM_H +#ifndef SOCKET_RUNTIME_PLATFORM_SYSTEM_H +#define SOCKET_RUNTIME_PLATFORM_SYSTEM_H #if SOCKET_RUNTIME_PLATFORM_WANTS_MINGW #include <_mingw.h> @@ -126,28 +126,11 @@ #include #endif +#undef SOCKET_RUNTIME_PLATFORM_H #include -#include "string.hh" #include "types.hh" #if SOCKET_RUNTIME_PLATFORM_ANDROID #include "android.hh" #endif - -namespace SSC { - struct RuntimePlatform { - const String arch; - const String os; - bool mac = false; - bool ios = false; - bool win = false; - bool android = false; - bool linux = false; - bool unix = false; - }; - - extern const RuntimePlatform platform; - void msleep (uint64_t ms); - uint64_t rand64 (); -} #endif diff --git a/src/platform/types.hh b/src/runtime/platform/types.hh similarity index 78% rename from src/platform/types.hh rename to src/runtime/platform/types.hh index de7387624e..cd5281dc32 100644 --- a/src/platform/types.hh +++ b/src/runtime/platform/types.hh @@ -12,6 +12,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -22,11 +25,15 @@ #include #endif -namespace SSC { +// ambient +namespace ssc { namespace fs = std::filesystem; +} +namespace ssc::runtime { using AtomicBool = std::atomic; using AtomicInt = std::atomic; + using BinarySemaphore = std::binary_semaphore; using String = std::string; using StringStream = std::stringstream; using WString = std::wstring; @@ -35,7 +42,7 @@ namespace SSC { using OutputFileStream = std::ofstream; using Mutex = std::recursive_mutex; using Lock = std::lock_guard; - using Map = std::map; + using ScopedLock = std::scoped_lock; using Path = fs::path; using Thread = std::thread; using Exception = std::exception; @@ -44,10 +51,13 @@ namespace SSC { template using Atomic = std::atomic; template using Array = std::array; template using Queue = std::queue; + template using Map = std::map; + template using UnorderedMap = std::unordered_map; template using Vector = std::vector; template using Function = std::function; template using Tuple = std::tuple; template using Set = std::set; + template using UnorderedSet = std::unordered_set; template using SharedPointer = std::shared_ptr; template using UniquePointer = std::unique_ptr; template using Promise = std::promise; diff --git a/src/core/process.hh b/src/runtime/process.hh similarity index 95% rename from src/core/process.hh rename to src/runtime/process.hh index f7f925bf42..9f63f1e1b4 100644 --- a/src/core/process.hh +++ b/src/runtime/process.hh @@ -1,7 +1,8 @@ -#ifndef SOCKET_RUNTIME_CORE_PROCESS_H -#define SOCKET_RUNTIME_CORE_PROCESS_H +#ifndef SOCKET_RUNTIME_RUNTIME_PROCESS_H +#define SOCKET_RUNTIME_RUNTIME_PROCESS_H -#include "../platform/platform.hh" +#include "platform.hh" +#include "string.hh" #if SOCKET_RUNTIME_PLATFORM_WINDOWS #include @@ -19,7 +20,7 @@ #define WEXITSTATUS(w) (((w) & 0xff00) >> 8) #endif -namespace SSC { +namespace ssc::runtime::process { struct ExecOutput { String output; int exitCode = 0; @@ -159,7 +160,7 @@ namespace SSC { Process( const String &command, const String &argv, - const SSC::Vector &env, + const Vector &env, const String &path = String(""), MessageCallback readStdout = nullptr, MessageCallback readStderr = nullptr, @@ -217,7 +218,7 @@ namespace SSC { void closeStdin () noexcept; PID open () noexcept { if (this->command.size() == 0) return 0; - auto str = trim(this->command + " " + this->argv); + auto str = string::trim(this->command + " " + this->argv); auto pid = open(str, this->path); read(); return pid; @@ -257,4 +258,8 @@ namespace SSC { #endif }; } + +namespace ssc::runtime { + using Process = process::Process; +} #endif diff --git a/src/core/process/unix.cc b/src/runtime/process/unix.cc similarity index 99% rename from src/core/process/unix.cc rename to src/runtime/process/unix.cc index 307bb115cc..c589b864e7 100644 --- a/src/core/process/unix.cc +++ b/src/runtime/process/unix.cc @@ -11,13 +11,15 @@ #include #include -#include "../process.hh" #include "../debug.hh" -#include "../modules/timers.hh" +#include "../string.hh" +#include "../process.hh" + +using ssc::runtime::string::splitc; extern char **environ; -namespace SSC { +namespace ssc::runtime::process { static StringStream initial; Process::Data::Data () noexcept diff --git a/src/core/process/win.cc b/src/runtime/process/win.cc similarity index 99% rename from src/core/process/win.cc rename to src/runtime/process/win.cc index ec8ea5c454..8d6edc1829 100644 --- a/src/core/process/win.cc +++ b/src/runtime/process/win.cc @@ -6,7 +6,7 @@ #include "../process.hh" #include "../env.hh" -namespace SSC { +namespace ssc::runtime::process { const static StringStream initial; diff --git a/src/runtime/queued_response.hh b/src/runtime/queued_response.hh new file mode 100644 index 0000000000..bde6f59455 --- /dev/null +++ b/src/runtime/queued_response.hh @@ -0,0 +1,92 @@ +#ifndef SOCKET_RUNTIME_QUEUED_RESPONSE_H +#define SOCKET_RUNTIME_QUEUED_RESPONSE_H + +#include "http.hh" +#include "crypto.hh" +#include "platform.hh" + +namespace ssc::runtime { + /** + * QueuedResponse represents a response object that is typically + * queued for sending back to a client. It may hold simple data or + * pointers to SSE/streaming callback mechanisms. + */ + struct QueuedResponse { + /** + * Numeric ID for this response entry in the queue. + */ + using ID = uint64_t; + + /** + * A callback used for an Event Stream (e.g., SSE, "Server-Sent Events"). + * Return `true` if sending succeeded, `false` if an error or stop occurred. + * + * Parameters: + * - name: event name + * - data: event data + * - finished: whether this is the final event + */ + using EventStreamCallback = Function; + + /** + * A callback used for chunked transfer encoding. + * Return `true` if sending succeeded, `false` if an error or stop occurred. + * + * Parameters: + * - chunk: pointer to chunk data + * - size: size of this chunk + * - finished: whether this is the final chunk + */ + using ChunkStreamCallback = Function; + + /** + * Identifies this queued response uniquely. + */ + ID id = crypto::rand64(); + + /** + * Optional time-to-live or expiration for this response, in milliseconds + * or similar. Zero means "no expiration." + */ + uint64_t ttl = 0; + + /** + * Pointer to the raw response body (if not streaming). + */ + SharedPointer body = nullptr; + + /** + * Length of the response body in bytes. + */ + size_t length = 0; + + /** + * HTTP headers sent in queued response + */ + http::Headers headers; + + /** + * (Optional) If this response is associated with a Worker, + * store the Worker ID here. + */ + String workerId = ""; + + /** + * Callback for SSE-like event stream. If set, we interpret the response + * as an event stream. The callback is responsible for sending each event. + */ + SharedPointer eventStreamCallback = nullptr; + + /** + * Callback for chunked transfer encoding. If set, we interpret the response + * as a chunked stream. The callback is responsible for sending each chunk. + */ + SharedPointer chunkStreamCallback = nullptr; + }; + + /** + * Container alias for storing multiple QueuedResponse entries by ID. + */ + using QueuedResponses = Map; +} +#endif diff --git a/src/runtime/resource.hh b/src/runtime/resource.hh new file mode 100644 index 0000000000..c6d6b122f4 --- /dev/null +++ b/src/runtime/resource.hh @@ -0,0 +1,28 @@ +#ifndef SOCKET_RUNTIME_RESOURCE_H +#define SOCKET_RUNTIME_RESOURCE_H + +#include "platform.hh" +#include "debug.hh" +#include "url.hh" + +namespace ssc::runtime { + class Resource { + public: + Atomic accessing = false; + String name; + String type; + debug::Tracer tracer; + Resource (const String& type, const String& name) + : name(name), + type(type), + tracer(name) + {} + virtual ~Resource () {} + virtual bool hasAccess () const noexcept { + return this->accessing; + } + virtual bool startAccessing () = 0; + virtual bool stopAccessing () = 0; + }; +} +#endif diff --git a/src/runtime/runtime.cc b/src/runtime/runtime.cc new file mode 100644 index 0000000000..4e67e638d4 --- /dev/null +++ b/src/runtime/runtime.cc @@ -0,0 +1,80 @@ +#include "runtime.hh" + +using namespace ssc::runtime::core; +using namespace ssc::runtime::config; + +namespace ssc::runtime { + Runtime::Runtime (const Options& options) + : windowManager() + dispatcher(*this), + options(options), + services(*this, { dispatcher, options.features }) + { + this->init(); + } + + Runtime::~Runtime() { + this->destroy(); + } + + void Runtime::init () { + this->start(); + } + + bool Runtime::start () { + if (!this->loop.start()) return false; + return this->resume(); + } + + bool Runtime::stop () { + if (!this->pause() || !this->loop.stop()) { + return false; + } + + return true; + } + + bool Runtime::resume () { + if (!this->loop.resume()) { + return false; + } + + if (!this->services.start()) { + return false; + } + + return true; + } + + bool Runtime::pause () { + if (!this->services.stop()) { + return false; + } + + #if !SOCKET_RUNTIME_PLATFORM_ANDROID + return this->loop.pause(); + #endif + + return true; + } + + bool Runtime::destroy () { + if (this->stop() && this->loop.shutdown()) { + return true; + } + + return false; + } + + bool Runtime::dispatch (const DispatchCallback& callback) { + return this->dispatcher.dispatch(callback); + } + + bool Runtime::stopped () const { + return this->loop.stopped(); + } + + bool Runtime::paused () const { + return this->loop.paused(); + } +} diff --git a/src/runtime/runtime.hh b/src/runtime/runtime.hh new file mode 100644 index 0000000000..03fd1ba0f4 --- /dev/null +++ b/src/runtime/runtime.hh @@ -0,0 +1,56 @@ +#ifndef SOCKET_RUNTIME_RUNTIME_H +#define SOCKET_RUNTIME_RUNTIME_H + +#include "core.hh" +#include "config.hh" +#include "bridge.hh" +#include "window.hh" +#include "webview.hh" +#include "options.hh" +#include "context.hh" + +#include "core/services.hh" + +namespace ssc::runtime { + class Runtime : public context::RuntimeContext { + public: + using DispatchCallback = Function; + using UserConfig = Map; + + struct Options : public ssc::runtime::Options { + UserConfig userConfig = config::getUserConfig(); + loop::Loop::Options loop; + core::Services::Features features; + }; + + // managers + window::Manager windowManager; + bridge::Manager bridgeManager; + + context::Dispatcher dispatcher; + UserConfig userConfig; + core::Services services; + Options options; + + Runtime (const Options&); + Runtime () = delete; + Runtime (const Runtime&) = delete; + Runtime (Runtime&&) = delete; + virtual ~Runtime(); + + Runtime& operator = (const Runtime&) = delete; + Runtime& operator = (Runtime&&) = delete; + + void init (); + bool start (); + bool stop (); + bool resume (); + bool pause(); + bool destroy (); + bool dispatch (const DispatchCallback&); + + bool stopped () const; + bool paused () const; + }; +} +#endif diff --git a/src/serviceworker/container.cc b/src/runtime/serviceworker/container.cc similarity index 75% rename from src/serviceworker/container.cc rename to src/runtime/serviceworker/container.cc index 9d9c5eef3a..caea95d754 100644 --- a/src/serviceworker/container.cc +++ b/src/runtime/serviceworker/container.cc @@ -82,12 +82,16 @@ namespace SSC { return *this; } - const JSON::Object ServiceWorkerContainer::Registration::json () const { + const JSON::Object ServiceWorkerContainer::Registration::json ( + bool includeSerializedWorkerArgs + ) const { return JSON::Object::Entries { {"id", std::to_string(this->id)}, {"scriptURL", this->scriptURL}, {"scope", this->options.scope}, - {"state", this->getStateString()} + {"state", this->getStateString()}, + {"scheme", this->options.scheme}, + {"serializedWorkerArgs", includeSerializedWorkerArgs ? encodeURIComponent(this->options.serializedWorkerArgs) : ""} }; } @@ -194,6 +198,18 @@ namespace SSC { this->bridge = bridge; this->isReady = true; + auto env = JSON::Object::Entries {}; + for (const auto& entry : bridge->userConfig) { + if (entry.first.starts_with("env_")) { + env[entry.first.substr(4)] = entry.second; + } else if (entry.first == "build_env") { + const auto keys = parseStringList(entry.second, { ',', ' ' }); + for (const auto& key : keys) { + env[key] = Env::get(key); + } + } + } + if (bridge->userConfig["webview_service-workers"].size() > 0) { const auto scripts = split(bridge->userConfig["webview_service-workers"], " "); for (const auto& value : scripts) { @@ -224,6 +240,18 @@ namespace SSC { scope, scriptURL, "*", + encodeURIComponent(JSON::Object(JSON::Object::Entries { + {"index", bridge->index}, + {"env", env}, + {"debug", isDebugEnabled()}, + {"config", bridge->userConfig}, + {"headless", bridge->userConfig["build_headless"] == "true"}, + {"conduit", JSON::Object::Entries { + {"port", bridge->core->conduit.port}, + {"hostname", bridge->core->conduit.hostname}, + {"sharedKey", bridge->core->conduit.sharedKey} + }} + }).str()), id } }); @@ -260,6 +288,18 @@ namespace SSC { scope, scriptURL, "*", + encodeURIComponent(JSON::Object(JSON::Object::Entries { + {"index", bridge->index}, + {"env", env}, + {"debug", isDebugEnabled()}, + {"headless", bridge->userConfig["build_headless"] == "true"}, + {"config", bridge->userConfig}, + {"conduit", JSON::Object::Entries { + {"port", bridge->core->conduit.port}, + {"hostname", bridge->core->conduit.hostname}, + {"sharedKey", bridge->core->conduit.sharedKey} + }} + }).str()), id } }); @@ -304,6 +344,18 @@ namespace SSC { scope, scriptURL, scheme, + encodeURIComponent(JSON::Object(JSON::Object::Entries { + {"index", bridge->index}, + {"env", env}, + {"debug", isDebugEnabled()}, + {"config", bridge->userConfig}, + {"headless", bridge->userConfig["build_headless"] == "true"}, + {"conduit", JSON::Object::Entries { + {"port", bridge->core->conduit.port}, + {"hostname", bridge->core->conduit.hostname}, + {"sharedKey", bridge->core->conduit.sharedKey} + }} + }).str()), id } }); @@ -346,8 +398,9 @@ namespace SSC { reply(IPC::Result { message.seq, message, JSON::Object {}, post }); }); - this->bridge->router.map("serviceWorker.fetch.response", [this](auto message, auto router, auto reply) mutable { + this->bridge->router.map("serviceWorker.fetch.response.write", [this](auto message, auto router, auto reply) mutable { FetchCallback callback; + FetchResponse response; FetchRequest request; ID clientId = 0; ID id = 0; @@ -398,18 +451,99 @@ namespace SSC { }}); } + do { + Lock lock(this->mutex); + if (this->fetchResponses.contains(id)) { + response = this->fetchResponses.at(id); + } else { + const auto headers = Headers(message.get("headers")); + response = FetchResponse { + id, + statusCode, + headers, + {}, + { clientId } + }; + } + + response.body.push(message.buffer.bytes, message.buffer.size); + this->fetchResponses.insert_or_assign(id, response); + } while (0); + + reply(IPC::Result { message.seq, message }); + }); + + this->bridge->router.map("serviceWorker.fetch.response.finish", [this](auto message, auto router, auto reply) mutable { + FetchCallback callback; + FetchResponse response; + FetchRequest request; + ID clientId = 0; + ID id = 0; + + int statusCode = 200; const auto headers = Headers(message.get("headers")); - auto response = FetchResponse { - id, - statusCode, - headers, - { message.buffer.size, message.buffer.bytes }, - { clientId } - }; - // XXX(@jwerle): we handle this in the android runtime + try { + id = std::stoull(message.get("id")); + } catch (...) { + return reply(IPC::Result::Err { message, JSON::Object::Entries { + {"message", "Invalid 'id' given in parameters"} + }}); + } + + try { + clientId = std::stoull(message.get("clientId")); + } catch (...) { + return reply(IPC::Result::Err { message, JSON::Object::Entries { + {"message", "Invalid 'clientId' given in parameters"} + }}); + } + + do { + Lock lock(this->mutex); + if (!this->fetchCallbacks.contains(id)) { + return reply(IPC::Result::Err { message, JSON::Object::Entries { + {"type", "NotFoundError"}, + {"message", "Callback 'id' given in parameters does not have a 'FetchCallback'"} + }}); + } + + if (!this->fetchRequests.contains(id)) { + return reply(IPC::Result::Err { message, JSON::Object::Entries { + {"type", "NotFoundError"}, + {"message", "Callback 'id' given in parameters does not have a 'FetchRequest'"} + }}); + } + + if (this->fetchResponses.contains(id)) { + response = this->fetchResponses.at(id); + } else { + response = FetchResponse { + id, + statusCode, + headers, + {}, + { clientId } + }; + } + + response.body.push(message.buffer.bytes, message.buffer.size); + + callback = this->fetchCallbacks.at(id); + request = this->fetchRequests.at(id); + } while (0); + + try { + statusCode = std::stoi(message.get("statusCode")); + } catch (...) { + return reply(IPC::Result::Err { message, JSON::Object::Entries { + {"message", "Invalid 'statusCode' given in parameters"} + }}); + } + + // XXX(@jwerle): we handle this in the android runtime const auto extname = Path(request.pathname).extension().string(); - auto html = (message.buffer.bytes != nullptr && message.buffer.size > 0) + auto html = (response.body.bytes != nullptr && response.body.size > 0) ? String(response.body.bytes.get(), response.body.size) : String(""); @@ -457,6 +591,10 @@ namespace SSC { if (this->fetchRequests.contains(id)) { this->fetchRequests.erase(id); } + + if (this->fetchResponses.contains(id)) { + this->fetchResponses.erase(id); + } }); } @@ -497,7 +635,7 @@ namespace SSC { const auto& registration = this->registrations.at(scope); if (this->bridge != nullptr) { - this->bridge->emit("serviceWorker.register", registration.json().str()); + this->bridge->emit("serviceWorker.register", registration.json(true).str()); } return registration; @@ -513,6 +651,7 @@ namespace SSC { scope, options.scriptURL, options.scheme, + options.serializedWorkerArgs, id } }); @@ -520,9 +659,7 @@ namespace SSC { const auto& registration = this->registrations.at(scope); if (this->bridge != nullptr) { - App::sharedApplication()->core->setImmediate([&, this]() { - this->bridge->emit("serviceWorker.register", registration.json().str()); - }); + this->bridge->emit("serviceWorker.register", registration.json(true)); } return registration; @@ -537,7 +674,7 @@ namespace SSC { if (this->registrations.contains(scope)) { const auto& registration = this->registrations.at(scope); if (this->bridge != nullptr) { - return this->bridge->emit("serviceWorker.unregister", registration.json().str()); + return this->bridge->emit("serviceWorker.unregister", registration.json()); } this->registrations.erase(scope); @@ -624,6 +761,14 @@ namespace SSC { } bool ServiceWorkerContainer::fetch (const FetchRequest& request, FetchCallback callback) { + return this->fetch(request, {}, callback); + } + + bool ServiceWorkerContainer::fetch ( + const FetchRequest& request, + const FetchOptions& options, + FetchCallback callback + ) { Lock lock(this->mutex); String pathname = request.pathname; String scope; @@ -636,11 +781,6 @@ namespace SSC { return false; } - // TODO(@jwerle): this prevents nested service worker fetches - do we want to prevent this? - if (request.headers.get("runtime-worker-type") == "serviceworker") { - return false; - } - for (const auto& entry : this->registrations) { const auto& registration = entry.second; @@ -663,6 +803,7 @@ namespace SSC { if (scope.size() > 0 && this->registrations.contains(scope)) { auto& registration = this->registrations.at(scope); if ( + options.waitForRegistrationToFinish && !registration.isActive() && ( registration.options.scheme == "*" || @@ -673,25 +814,25 @@ namespace SSC { registration.state == Registration::State::Registered ) ) { - this->core->dispatchEventLoop([this, request, callback, ®istration]() { - const auto interval = this->core->setInterval(8, [this, request, callback, ®istration] (auto cancel) { + const auto app = App::sharedApplication(); + this->core->dispatchEventLoop([this, app, request, callback, ®istration]() { + const auto interval = this->core->setInterval(8, [this, app, request, callback, ®istration] (auto cancel) { if (registration.state == Registration::State::Activated) { + if (!this->fetch(request, callback)) { + debug( + #if SOCKET_RUNTIME_PLATFORM_APPLE + "ServiceWorkerContainer: Failed to dispatch fetch request '%s %s%s' for client '%llu'", + #else + "ServiceWorkerContainer: Failed to dispatch fetch request '%s %s%s' for client '%lu'", + #endif + request.method.c_str(), + request.pathname.c_str(), + (request.query.size() > 0 ? String("?") + request.query : String("")).c_str(), + request.client.id + ); + } + cancel(); - this->core->setTimeout(8, [this, request, callback, ®istration] { - if (!this->fetch(request, callback)) { - debug( - #if SOCKET_RUNTIME_PLATFORM_APPLE - "ServiceWorkerContainer: Failed to dispatch fetch request '%s %s%s' for client '%llu'", - #else - "ServiceWorkerContainer: Failed to dispatch fetch request '%s %s%s' for client '%lu'", - #endif - request.method.c_str(), - request.pathname.c_str(), - (request.query.size() > 0 ? String("?") + request.query : String("")).c_str(), - request.client.id - ); - } - }); } }); diff --git a/src/serviceworker/container.hh b/src/runtime/serviceworker/container.hh similarity index 78% rename from src/serviceworker/container.hh rename to src/runtime/serviceworker/container.hh index 530d965c62..aa13fe7b0e 100644 --- a/src/serviceworker/container.hh +++ b/src/runtime/serviceworker/container.hh @@ -3,6 +3,7 @@ #include "../core/headers.hh" #include "../core/json.hh" +#include "../ipc/message.hh" #include "../ipc/client.hh" #include "protocols.hh" @@ -24,6 +25,7 @@ namespace SSC { String scope; String scriptURL; String scheme = "*"; + String serializedWorkerArgs = ""; ID id = 0; }; @@ -40,7 +42,7 @@ namespace SSC { }; struct Storage { - Map data; + Map data; const JSON::Object json () const; void set (const String& key, const String& value); const String get (const String& key) const; @@ -67,16 +69,21 @@ namespace SSC { Registration& operator= (const Registration&); Registration& operator= (Registration&&); - const JSON::Object json () const; + const JSON::Object json (bool includeSerializedWorkerArgs = false) const; bool isActive () const; bool isWaiting () const; bool isInstalling () const; const String getStateString () const; }; - struct FetchBody { - size_t size = 0; - SharedPointer bytes = nullptr; + struct FetchBody : public IPC::MessageBuffer { + FetchBody () = default; + FetchBody (size_t size, SharedPointer bytes) + : IPC::MessageBuffer(bytes, size) + {} + FetchBody (SharedPointer bytes, size_t size) + : IPC::MessageBuffer(bytes, size) + {} }; struct FetchRequest { @@ -100,10 +107,15 @@ namespace SSC { Client client; }; + struct FetchOptions { + bool waitForRegistrationToFinish = true; + }; + using Registrations = std::map; using FetchCallback = Function; using FetchCallbacks = std::map; using FetchRequests = std::map; + using FetchResponses = std::map; SharedPointer core = nullptr; IPC::Bridge* bridge = nullptr; @@ -114,6 +126,7 @@ namespace SSC { Registrations registrations; FetchRequests fetchRequests; + FetchResponses fetchResponses; FetchCallbacks fetchCallbacks; static ServiceWorkerContainer* sharedContainer (); @@ -127,7 +140,15 @@ namespace SSC { bool unregisterServiceWorker (String scopeOrScriptURL); void skipWaiting (ID id); void updateState (ID id, const String& stateString); - bool fetch (const FetchRequest& request, FetchCallback callback); + bool fetch ( + const FetchRequest& request, + FetchCallback callback + ); + bool fetch ( + const FetchRequest& request, + const FetchOptions& options, + FetchCallback callback + ); bool claimClients (const String& scope); }; } diff --git a/src/serviceworker/protocols.cc b/src/runtime/serviceworker/protocols.cc similarity index 100% rename from src/serviceworker/protocols.cc rename to src/runtime/serviceworker/protocols.cc diff --git a/src/serviceworker/protocols.hh b/src/runtime/serviceworker/protocols.hh similarity index 95% rename from src/serviceworker/protocols.hh rename to src/runtime/serviceworker/protocols.hh index 35191871e4..d3ddf27d00 100644 --- a/src/serviceworker/protocols.hh +++ b/src/runtime/serviceworker/protocols.hh @@ -16,7 +16,7 @@ namespace SSC { Data data; }; - using Mapping = std::map; + using Mapping = Map; ServiceWorkerContainer* serviceWorkerContainer; Mapping mapping; diff --git a/src/platform/string.hh b/src/runtime/string.hh similarity index 89% rename from src/platform/string.hh rename to src/runtime/string.hh index 578d61e58c..35dd48d931 100644 --- a/src/platform/string.hh +++ b/src/runtime/string.hh @@ -1,8 +1,8 @@ -#ifndef SOCKET_RUNTIME_PLATFORM_STRING_H -#define SOCKET_RUNTIME_PLATFORM_STRING_H +#ifndef SOCKET_RUNTIME_STRING_H +#define SOCKET_RUNTIME_STRING_H #include -#include "types.hh" +#include "platform.hh" /** * Converts a literal expression to an inline string: @@ -11,11 +11,11 @@ #define _CONVERT_TO_STRING(value) #value #define CONVERT_TO_STRING(value) _CONVERT_TO_STRING(value) -namespace SSC { +namespace ssc::runtime::string { // transform String replace (const String& source, const String& regex, const String& value); String replace (const String& source, const std::regex& regex, const String& value); - String tmpl (const String& source, const Map& variables); + String tmpl (const String& source, const Map& variables); String trim (String source); String toLowerCase (const String& source); String toUpperCase (const String& source); @@ -43,5 +43,4 @@ namespace SSC { String formatWindowsError (DWORD error, const String& source); #endif } - #endif diff --git a/src/platform/string.cc b/src/runtime/string/string.cc similarity index 97% rename from src/platform/string.cc rename to src/runtime/string/string.cc index 266f6c2689..996c975ae9 100644 --- a/src/platform/string.cc +++ b/src/runtime/string/string.cc @@ -1,11 +1,10 @@ -#include "platform.hh" -#include "string.hh" +#include "../string.hh" #if defined(min) #undef min #endif -namespace SSC { +namespace ssc::runtime::string { String replace (const String& source, const std::regex& regex, const String& value) { return std::regex_replace(source, regex, value); } @@ -14,7 +13,7 @@ namespace SSC { return replace(source, std::regex(regex), value); } - String tmpl (const String& source, const Map& variables) { + String tmpl (const String& source, const Map& variables) { auto output = source; for (const auto& tuple : variables) { diff --git a/src/core/socket.hh b/src/runtime/udp.hh similarity index 76% rename from src/core/socket.hh rename to src/runtime/udp.hh index ac9d403417..d61f26e996 100644 --- a/src/core/socket.hh +++ b/src/runtime/udp.hh @@ -1,11 +1,13 @@ -#ifndef SOCKET_RUNTIME_CORE_SOCKET_H -#define SOCKET_RUNTIME_CORE_SOCKET_H +#ifndef SOCKET_RUNTIME_UDP_H +#define SOCKET_RUNTIME_UDP_H -#include "../platform/platform.hh" -#include "post.hh" +#include "queued_response.hh" +#include "platform.hh" +#include "udp/ip.hh" +#include "loop.hh" -namespace SSC { - class Core; +namespace ssc::runtime::udp { + class SocketManager; typedef enum { SOCKET_TYPE_NONE = 0, SOCKET_TYPE_TCP = 1 << 1, @@ -68,7 +70,7 @@ namespace SSC { class Socket { public: struct RequestContext { - using Callback = Function; + using Callback = Function; SharedPointer bytes = nullptr; size_t size = 0; uv_buf_t buffer; @@ -108,7 +110,7 @@ namespace SSC { // instance state uint64_t id = 0; Mutex mutex; - Core *core = nullptr; + SocketManager* manager = nullptr; struct { struct { @@ -127,7 +129,7 @@ namespace SSC { /** * Private `Socket` class constructor */ - Socket (Core *core, socket_type_t peerType, uint64_t peerId, bool isEphemeral); + Socket (SocketManager* manager, socket_type_t peerType, uint64_t peerId, bool isEphemeral); ~Socket (); int init (); @@ -158,7 +160,7 @@ namespace SSC { size_t size, int port, const String& address, - const Socket::RequestContext::Callback& callback + const Socket::RequestContext::Callback callback ); int recvstart (); int recvstart (UDPReceiveCallback onrecv); @@ -168,5 +170,43 @@ namespace SSC { void close (); void close (Function onclose); }; + + class SocketManager { + public: + using ID = uint64_t; + using Map = Map>; + + struct Options { + loop::Loop& loop; + }; + + Mutex mutex; + Map sockets; + loop::Loop& loop; + + SocketManager (const Options&); + SocketManager () = delete; + SocketManager (const SocketManager&) = delete; + SocketManager (SocketManager&&) = delete; + virtual ~SocketManager (); + + void resume (); + void pause (); + bool has (ID id); + void remove (ID id); + void remove (ID id, bool autoClose); + + SharedPointer get (ID id); + SharedPointer create ( + socket_type_t type, + ID id + ); + + SharedPointer create ( + socket_type_t type, + ID id, + bool isEphemeral + ); + }; } #endif diff --git a/src/core/ip.hh b/src/runtime/udp/ip.hh similarity index 83% rename from src/core/ip.hh rename to src/runtime/udp/ip.hh index 065dd715eb..b0a0dc8d0c 100644 --- a/src/core/ip.hh +++ b/src/runtime/udp/ip.hh @@ -1,9 +1,9 @@ -#ifndef SOCKET_RUNTIME_CORE_IP_H -#define SOCKET_RUNTIME_CORE_IP_H +#ifndef SOCKET_RUNTIME_UDP_IP_H +#define SOCKET_RUNTIME_UDP_IP_H -#include "../platform/platform.hh" +#include "../platform.hh" -namespace SSC::IP { +namespace ssc::runtime::udp::ip { static inline String addrToIPv4 (struct sockaddr_in* sin) { char buf[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &sin->sin_addr, buf, INET_ADDRSTRLEN); diff --git a/src/runtime/udp/manager.cc b/src/runtime/udp/manager.cc new file mode 100644 index 0000000000..2d9ded4cde --- /dev/null +++ b/src/runtime/udp/manager.cc @@ -0,0 +1,88 @@ +#include "../udp.hh" + +namespace ssc::runtime::udp { + SocketManager::SocketManager (const Options& options) + : loop(options.loop) + {} + + SocketManager::~SocketManager () {} + + void SocketManager::resume () { + Lock lock(this->mutex); + this->loop.dispatch([=, this]() { + for (const auto& entry : this->sockets) { + auto& socket = entry.second; + if (socket != nullptr && (socket->isBound() || socket->isConnected())) { + socket->resume(); + } + } + }); + } + + void SocketManager::pause () { + Lock lock(this->mutex); + for (const auto& entry : this->sockets) { + auto& socket = entry.second; + if (socket != nullptr && (socket->isBound() || socket->isConnected())) { + socket->pause(); + } + } + } + + bool SocketManager::has (ID id) { + Lock lock(this->mutex); + return this->sockets.find(id) != this->sockets.end(); + } + + void SocketManager::remove (ID id) { + return this->remove(id, false); + } + + void SocketManager::remove (ID id, bool autoClose) { + if (this->has(id)) { + if (autoClose) { + auto socket = this->get(id); + if (socket != nullptr) { + socket->close(); + } + } + + Lock lock(this->mutex); + this->sockets.erase(id); + } + } + + SharedPointer SocketManager::get (ID id) { + if (this->has(id)) { + return this->sockets[id]; + } + + return nullptr; + } + + SharedPointer SocketManager::create (socket_type_t type, ID id) { + return this->create(type, id, false); + } + + SharedPointer SocketManager::create ( + socket_type_t type, + ID id, + bool isEphemeral + ) { + if (this->has(id)) { + auto socket = this->get(id); + if (socket != nullptr) { + if (isEphemeral) { + Lock lock(socket->mutex); + socket->flags = (udp::socket_flag_t) (socket->flags | udp::SOCKET_FLAG_EPHEMERAL); + } + } + + return socket; + } + + Lock lock(this->mutex); + this->sockets[id] = std::make_shared(this, type, id, isEphemeral); + return this->sockets.at(id); + } +} diff --git a/src/core/socket.cc b/src/runtime/udp/socket.cc similarity index 94% rename from src/core/socket.cc rename to src/runtime/udp/socket.cc index f9357fb0fd..afc4941943 100644 --- a/src/core/socket.cc +++ b/src/runtime/udp/socket.cc @@ -1,9 +1,6 @@ -#include "modules/udp.hh" -#include "socket.hh" -#include "core.hh" -#include "ip.hh" +#include "../udp.hh" -namespace SSC { +namespace ssc::runtime::udp { int LocalPeerInfo::getsockname (uv_udp_t *socket, struct sockaddr *addr) { int namelen = sizeof(struct sockaddr_storage); return uv_udp_getsockname(socket, addr, &namelen); @@ -41,11 +38,11 @@ namespace SSC { void LocalPeerInfo::init (const struct sockaddr_storage *addr) { if (addr->ss_family == AF_INET) { this->family = "IPv4"; - this->address = IP::addrToIPv4((struct sockaddr_in*) addr); + this->address = udp::ip::addrToIPv4((struct sockaddr_in*) addr); this->port = (int) htons(((struct sockaddr_in*) addr)->sin_port); } else if (addr->ss_family == AF_INET6) { this->family = "IPv6"; - this->address = IP::addrToIPv6((struct sockaddr_in6*) addr); + this->address = udp::ip::addrToIPv6((struct sockaddr_in6*) addr); this->port = (int) htons(((struct sockaddr_in6*) addr)->sin6_port); } } @@ -87,25 +84,24 @@ namespace SSC { void RemotePeerInfo::init (const struct sockaddr_storage *addr) { if (addr->ss_family == AF_INET) { this->family = "IPv4"; - this->address = IP::addrToIPv4((struct sockaddr_in*) addr); + this->address = udp::ip::addrToIPv4((struct sockaddr_in*) addr); this->port = (int) htons(((struct sockaddr_in*) addr)->sin_port); } else if (addr->ss_family == AF_INET6) { this->family = "IPv6"; - this->address = IP::addrToIPv6((struct sockaddr_in6*) addr); + this->address = udp::ip::addrToIPv6((struct sockaddr_in6*) addr); this->port = (int) htons(((struct sockaddr_in6*) addr)->sin6_port); } } Socket::Socket ( - Core *core, + SocketManager* manager, socket_type_t peerType, uint64_t peerId, bool isEphemeral - ) { - this->id = peerId; - this->type = peerType; - this->core = core; - + ) : manager(manager), + type(peerType), + id(peerId) + { if (isEphemeral) { this->flags = (socket_flag_t) (this->flags | SOCKET_FLAG_EPHEMERAL); } @@ -117,7 +113,7 @@ namespace SSC { int Socket::init () { Lock lock(this->mutex); - auto loop = this->core->getEventLoop(); + auto loop = this->manager->loop.get(); int err = 0; memset(&this->handle, 0, sizeof(this->handle)); @@ -347,7 +343,7 @@ namespace SSC { size_t size, int port, const String& address, - const Socket::RequestContext::Callback& callback + const Socket::RequestContext::Callback callback ) { Lock lock(this->mutex); int err = 0; @@ -359,7 +355,7 @@ namespace SSC { err = uv_ip4_addr((char *) address.c_str(), port, &this->addr); if (err) { - return callback(err, Post{}); + return callback(err, QueuedResponse{}); } } @@ -373,7 +369,7 @@ namespace SSC { auto ctx = reinterpret_cast(req->data); auto socket = ctx->socket; - ctx->callback(status, Post{}); + ctx->callback(status, QueuedResponse{}); if (socket->isEphemeral()) { socket->close(); @@ -384,7 +380,7 @@ namespace SSC { }); if (err < 0) { - ctx->callback(err, Post{}); + ctx->callback(err, QueuedResponse{}); if (this->isEphemeral()) { this->close(); @@ -503,7 +499,7 @@ namespace SSC { Lock lock(this->mutex); if (this->isClosed()) { - this->core->udp.removeSocket(this->id); + this->manager->remove(this->id); if (onclose != nullptr) { onclose(); } @@ -536,10 +532,9 @@ namespace SSC { onclose(); } - socket->core->udp.removeSocket(socket->id); + socket->manager->remove(socket->id); socket = nullptr; } }); } } -} diff --git a/src/runtime/unique_client.hh b/src/runtime/unique_client.hh new file mode 100644 index 0000000000..e00d38834e --- /dev/null +++ b/src/runtime/unique_client.hh @@ -0,0 +1,51 @@ +#ifndef SOCKET_RUNTIME_UNIQUE_CLIENT_H +#define SOCKET_RUNTIME_UNIQUE_CLIENT_H + +#include "platform.hh" + +namespace ssc::runtime { + struct UniqueClient { + using ID = uint64_t; + ID id = rand64(); + int index = -1; + + UniqueClient () = default; + UniqueClient (ID id) + : id(id) + {} + + UniqueClient (ID id, int index) + : id(id), index(index) + {} + + UniqueClient (const UniqueClient& client) + : id(client.id), + index(client.index) + {} + + UniqueClient (UniqueClient&& client) + : id(client.id), + index(client.index) + { + client.id = 0; + client.index = -1; + } + + virtual ~UniqueClient () {} + + UniqueClient& operator = (const UniqueClient& client) { + this->id = client.id; + this->index = client.index; + return *this; + } + + UniqueClient& operator = (UniqueClient&& client) { + this->id = client.id; + this->index = client.index; + client.id = 0; + client.index = -1; + return *this; + } + }; +} +#endif diff --git a/src/runtime/url.hh b/src/runtime/url.hh new file mode 100644 index 0000000000..c46b072201 --- /dev/null +++ b/src/runtime/url.hh @@ -0,0 +1,224 @@ +#ifndef SOCKET_RUNTIME_RUNTIME_URL_H +#define SOCKET_RUNTIME_RUNTIME_URL_H + +#include "json.hh" +#include "debug.hh" + +namespace ssc::runtime::url { + struct PathComponents { + using Iterator = Vector::const_iterator; + using const_iterator = Vector::const_iterator; + + Vector parts; + + PathComponents () = default; + PathComponents (const String&); + + const String operator [] (const size_t) const; + const String& operator [] (const size_t); + + void set (const String&); + const String& at (const size_t) const; + const String str () const noexcept; + const Iterator begin () const noexcept; + const size_t size () const noexcept; + const Iterator end () const noexcept; + const bool empty () const noexcept; + + template + const T get (const size_t) const; + }; + + class SearchParams { + public: + class Value : public JSON::Raw { + public: + bool decodeURIComponents = true; + + Value () = default; + Value (const Value&); + Value (Value&&); + Value (const String&); + Value (const JSON::Any&); + Value (const std::nullptr_t ); + + Value& operator = (const Value&); + Value& operator = (Value&&); + Value& operator = (const String&); + Value& operator = (const JSON::Any&); + Value& operator = (const std::nullptr_t&); + + const String str () const; + + template + const T as () const { + return T(this->value()); + } + }; + + using Entries = Map; + using const_iterator = Entries::const_iterator; + using iterator = Entries::iterator; + + struct Options { + bool decodeURIComponents = true; + }; + + Options options; + Map data; + + SearchParams () = default; + SearchParams (const Options&); + SearchParams (const SearchParams&); + SearchParams (SearchParams&&); + + SearchParams (const Map&, const Options& = { + .decodeURIComponents = true + }); + + SearchParams (const JSON::Object&, const Options& = { + .decodeURIComponents = true + }); + + SearchParams (const String&, const Options& = { + .decodeURIComponents = true + }); + + SearchParams& operator = (const SearchParams&); + SearchParams& operator = (SearchParams&&); + + Value operator [] (const String&) const; + Value& operator [] (const String&); + + SearchParams& set (const String&, const Value&); + SearchParams& set (const String&); + const Value& at (const String&) const; + const Value get (const String&) const; + const Value get (const String&); + bool contains (const String&) const; + Entries::size_type size () const; + const const_iterator begin () const noexcept; + const const_iterator end () const noexcept; + iterator begin () noexcept; + iterator end () noexcept; + + const String str () const; + const Map map () const; + const JSON::Object json () const; + }; + + struct URL { + struct Components { + String originalURL = ""; + String scheme = ""; + String authority = ""; + String pathname = ""; + String query = ""; + String fragment = ""; + + static const Components parse (const String&); + }; + + struct Builder { + String protocol = ""; + String username = ""; + String password = ""; + String hostname = ""; + String port = ""; + String pathname = ""; + String search = ""; // includes '?' and 'query' if 'query' is not empty + String hash = ""; // include '#' and 'fragment' if 'fragment' is not empty + + bool decodeURIComponents = false; + + Builder& setProtocol (const String&); + Builder& setUsername (const String&); + Builder& setPassword (const String&); + Builder& setHostname (const String&); + Builder& setPort (const String&); + Builder& setPort (const int); + Builder& setPathname (const String&); + Builder& setQuery (const String&); + Builder& setSearch (const String&); + Builder& setHash (const String&); + Builder& setFragment (const String&); + Builder& setSearchParam (const String&, const String&); + Builder& setSearchParam (const String&, const JSON::Any&); + Builder& setSearchParams (const Map&); + Builder& setDecodeURIComponents (bool); + + URL build () const; + }; + + // core properties + String href = ""; + String origin = ""; + String protocol = ""; + String username = ""; + String password = ""; + String hostname = ""; + String port = ""; + String pathname = ""; + String search = ""; // includes '?' and 'query' if 'query' is not empty + String hash = ""; // include '#' and 'fragment' if 'fragment' is not empty + + // extra properties + String scheme = ""; + String fragment = ""; + String query = ""; + + SearchParams searchParams; + PathComponents pathComponents; + + URL () = default; + URL (const URL&); + URL (URL&&); + URL (const String& href, bool decodeURIComponents = false); + URL (const JSON::Object& json); + + URL& operator = (const URL&); + URL& operator = (URL&&); + + void set (const String& href, bool decodeURIComponents = false); + void set (const JSON::Object& json); + const String str () const; + const char* c_str () const; + const JSON::Object json () const; + const size_t size () const; + }; + + size_t url_encode_uri_component ( + const char *input, + size_t inputSize, + char *output, + size_t outputSize + ); + + size_t url_decode_uri_component ( + const char *input, + size_t inputSize, + char *output, + size_t outputSize + ); + + /** + * Encodes input by replacing certain characters by + * one, two, three, or four escape sequences representing the UTF-8 + * encoding of the character. + * @param input The input string to encode + * @return An encoded string value + */ + String encodeURIComponent (const String& input); + + /** + * Decodes a value encoded with `encodeURIComponent` + * @param input The input string to decode + * @return A decoded string + */ + String decodeURIComponent (const String& input); +} + +namespace ssc::runtime { + using URL = url::URL; +} +#endif diff --git a/src/runtime/url/builder.cc b/src/runtime/url/builder.cc new file mode 100644 index 0000000000..b922fc3713 --- /dev/null +++ b/src/runtime/url/builder.cc @@ -0,0 +1,134 @@ +#include "../url.hh" + +namespace ssc::runtime::url { + URL::Builder& URL::Builder::setProtocol (const String& protocol) { + this->protocol = protocol; + return *this; + } + + URL::Builder& URL::Builder::setUsername (const String& username) { + this->username = username; + return *this; + } + + URL::Builder& URL::Builder::setPassword (const String& password) { + this->password = password; + return *this; + } + + URL::Builder& URL::Builder::setHostname (const String& hostname) { + this->hostname = hostname; + return *this; + } + + URL::Builder& URL::Builder::setPort (const String& port) { + this->port = port; + return *this; + } + + URL::Builder& URL::Builder::setPort (const int port) { + this->port = std::to_string(port); + return *this; + } + + URL::Builder& URL::Builder::setPathname (const String& pathname) { + this->pathname = pathname; + return *this; + } + + URL::Builder& URL::Builder::setQuery (const String& query) { + this->search = "?" + query; + return *this; + } + + URL::Builder& URL::Builder::setSearch (const String& search) { + this->search = search; + return *this; + } + + URL::Builder& URL::Builder::setHash (const String& hash) { + this->hash = hash; + return *this; + } + + URL::Builder& URL::Builder::setFragment (const String& fragment) { + this->hash = "#" + fragment; + return *this; + } + + URL::Builder& URL::Builder::setSearchParam (const String& key, const String& value) { + return this->setSearchParams(Map {{ key, value }}); + } + + URL::Builder& URL::Builder::setSearchParam (const String& key, const JSON::Any& value) { + if (JSON::typeof(value) == "string" || JSON::typeof(value) == "number" || JSON::typeof(value) == "boolean") { + return this->setSearchParam(key, value.str()); + } + + return *this; + } + + URL::Builder& URL::Builder::setSearchParams (const Map& params) { + if (params.size() > 0) { + if (!this->search.starts_with("?")) { + this->search = "?"; + } else if (this->search.size() > 0) { + this->search += "&"; + } + + for (const auto& entry : params) { + this->search += entry.first + "=" + entry.second + "&"; + } + } + + if (this->search.ends_with("&")) { + this->search = this->search.substr(0, this->search.size() - 1); + } + + return *this; + } + + URL::Builder& URL::Builder::setDecodeURIComponents (bool enabled) { + this->decodeURIComponents = enabled; + return *this; + } + + URL URL::Builder::build () const { + StringStream stream; + + if (this->protocol.size() == 0) { + return String(""); + } + + stream << this->protocol << ":"; + + if (this->hostname.size() > 0) { + stream << "//"; + if (this->username.size() > 0) { + stream << this->username; + if (this->password.size() > 0) { + stream << ":" << this->password; + } + + stream << "@" << this->hostname; + if (this->port.size() > 0) { + stream << ":" << this->port; + } + } else { + stream << this->hostname; + } + } + + if (this->hostname.size() > 0 && this->pathname.size() > 0) { + if (!this->pathname.starts_with("/")) { + stream << "/"; + } + } else { + stream << "/"; + } + + stream << this->pathname << this->search << this->hash; + + return stream.str(); + } +} diff --git a/src/runtime/url/codec.cc b/src/runtime/url/codec.cc new file mode 100644 index 0000000000..b77e89c426 --- /dev/null +++ b/src/runtime/url/codec.cc @@ -0,0 +1,172 @@ +#include "../url.hh" + +static const char HEX_CHARS_INDEX[16 + 1] = "0123456789ABCDEF"; + +static const signed char DEC_CHARS_INDEX[256] = { + /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ + /* 0 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* 1 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* 2 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* 3 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1, + + /* 4 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* 5 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* 6 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* 7 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + + /* 8 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* 9 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* A */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* B */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + + /* C */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* D */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* E */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + /* F */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1 +}; + +static const char SAFE_CHARS_INDEX[256] = { + /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ + /* 0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 1 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 2 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 3 */ 1,1,1,1, 1,1,1,1, 1,1,0,0, 0,0,0,0, + + /* 4 */ 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, + /* 5 */ 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0, + /* 6 */ 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, + /* 7 */ 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0, + + /* 8 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 9 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* A */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* B */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + + /* C */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* D */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* E */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* F */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 +}; + +namespace ssc::runtime::url { + size_t url_encode_uri_component ( + const char *input, + size_t inputSize, + char *output, + size_t outputSize + ) { + size_t i = 0; + size_t j = 0; + + if (!input || !output || outputSize == 0) { + return 0; + } + + while (i < inputSize) { + const auto c = (unsigned char)input[i++]; + if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { + if (j >= outputSize - 1) { + return 0; + } + + output[j++] = c; + } else { + if (j >= outputSize - 3) { + return 0; + } + + output[j++] = '%'; + output[j++] = HEX_CHARS_INDEX[c >> 4]; + output[j++] = HEX_CHARS_INDEX[c & 0x0F]; + } + } + + if (j >= outputSize) { + return 0; + } + + output[j] = 0; + return j; + } + + size_t url_decode_uri_component ( + const char *input, + size_t inputSize, + char *output, + size_t outputSize + ) { + size_t i = 0; + size_t j = 0; + + if (!input || !output || outputSize == 0) { + return 0; + } + + while (i < inputSize) { + if ( + input[i] == '%' && + i + 2 < inputSize && + isxdigit(input[i + 1]) && + isxdigit(input[i + 2]) + ) { + if (j >= outputSize - 1) { + return 0; + } + + const char high = input[i + 1]; + const char low = input[i + 2]; + + output[j++] = static_cast( + (isdigit(high) ? high - '0' : (toupper(high) - 'A' + 10)) << 4 | + (isdigit(low) ? low - '0' : (toupper(low) - 'A' + 10)) + ); + + i += 3; + } else if (input[i] == '+') { + if (j >= outputSize - 1) { + return 0; + } + + output[j++] = ' '; + i++; + } else { + if (j >= outputSize - 1) { + return 0; + } + + output[j++] = input[i++]; + } + } + + if (j >= outputSize) { + return 0; + } + + output[j] = 0; + return j; + } + + String encodeURIComponent (const String& input) { + // maximum size if all characters are encoded + Vector output(input.size() * 3 + 1); + const auto size = url_encode_uri_component( + input.c_str(), + input.size(), + output.data(), + output.size() + ); + return String(output.data(), size); + } + + String decodeURIComponent (const String& input) { + // decoded string is never longer than input + Vector output(input.size() + 1); + const auto size = url_decode_uri_component( + input.c_str(), + input.size(), + output.data(), + output.size() + ); + return String(output.data(), size); + } +} diff --git a/src/runtime/url/path.cc b/src/runtime/url/path.cc new file mode 100644 index 0000000000..6ea5342f06 --- /dev/null +++ b/src/runtime/url/path.cc @@ -0,0 +1,109 @@ +#include + +#include "../url.hh" +#include "../string.hh" + +using namespace ssc::runtime::string; + +namespace ssc::runtime::url { + PathComponents::PathComponents (const String& pathname) { + this->set(pathname); + } + + void PathComponents::set (const String& pathname) { + const auto parts = split(pathname, "/"); + for (const auto& part : parts) { + const auto value = trim(part); + if (value.size() > 0) { + this->parts.push_back(value); + } + } + } + + const String PathComponents::operator[] (const size_t index) const { + return this->parts[index]; + } + + const String& PathComponents::operator[] (const size_t index) { + return this->parts[index]; + } + + const String& PathComponents:: at (const size_t index) const { + return this->parts.at(index); + } + + const String PathComponents::str () const noexcept { + return "/" + join(this->parts, "/"); + } + + const PathComponents::Iterator PathComponents::begin () const noexcept { + return this->parts.begin(); + } + + const PathComponents::Iterator PathComponents::end () const noexcept { + return this->parts.end(); + } + + const size_t PathComponents::size () const noexcept { + return this->parts.size(); + } + + const bool PathComponents::empty () const noexcept { + return this->parts.empty(); + } + + template + const T PathComponents::get (const size_t index) const { + if (std::is_same::value) { + return std::stoull(this->at(index)); + } + + if (std::is_same::value) { + return std::stoll(this->at(index)); + } + + if (std::is_same::value) { + return std::stoul(this->at(index)); + } + + if (std::is_same::value) { + return std::stol(this->at(index)); + } + + if (std::is_same::value) { + return std::stoul(this->at(index)); + } + + if (std::is_same::value) { + return std::stol(this->at(index)); + } + + if (std::is_same::value) { + return std::stoul(this->at(index)); + } + + if (std::is_same::value) { + return std::stod(this->at(index)); + } + + if (std::is_same::value) { + if (std::stod(this->at(index)) == 0) { + return false; + } + + return true; + } + + throw Error("unhandled type in PathComponents::get"); + } + + template const uint64_t PathComponents::get (const size_t index) const; + template const int64_t PathComponents::get (const size_t index) const; + template const uint32_t PathComponents::get (const size_t index) const; + template const int32_t PathComponents::get (const size_t index) const; + template const uint16_t PathComponents::get (const size_t index) const; + template const int16_t PathComponents::get (const size_t index) const; + template const uint8_t PathComponents::get (const size_t index) const; + template const int8_t PathComponents::get (const size_t index) const; + template const bool PathComponents::get (const size_t index) const; +} diff --git a/src/runtime/url/search.cc b/src/runtime/url/search.cc new file mode 100644 index 0000000000..0fe8764dc8 --- /dev/null +++ b/src/runtime/url/search.cc @@ -0,0 +1,221 @@ +#include "../url.hh" +#include "../string.hh" + +using namespace ssc::runtime::string; + +namespace ssc::runtime::url { + SearchParams::Value::Value (const Value& value) + : JSON::Raw(std::move(value)) + { + this->decodeURIComponents = value.decodeURIComponents; + } + + SearchParams::Value::Value (Value&& value) + : JSON::Raw(std::move(value)) + { + this->decodeURIComponents = value.decodeURIComponents; + value.data = nullptr; + } + + SearchParams::Value::Value (const String& source) + : JSON::Raw(source) + {} + + SearchParams::Value::Value (const JSON::Any& source) + : JSON::Raw(std::move(source)) + {} + + SearchParams::Value::Value (const std::nullptr_t source) + : JSON::Raw(nullptr) + {} + + SearchParams::Value& SearchParams::Value::operator = (const Value& value) { + this->data = value.data; + this->decodeURIComponents = value.decodeURIComponents; + return *this; + } + + SearchParams::Value& SearchParams::Value::operator = (Value&& value) { + this->data = std::move(value.data); + this->decodeURIComponents = value.decodeURIComponents; + value.data = nullptr; + return *this; + } + + SearchParams::Value& SearchParams::Value::operator = (const String& value) { + this->data = value; + return *this; + } + + SearchParams::Value& SearchParams::Value::operator = (const JSON::Any& value) { + this->data = value; + return *this; + } + + SearchParams::Value& SearchParams::Value::operator = (const std::nullptr_t& value) { + this->data = nullptr; + return *this; + } + + const String SearchParams::Value::str () const { + if (this->data.size() > 0 && this->decodeURIComponents) { + return decodeURIComponent(this->data); + } + + return this->data; + } + + SearchParams::SearchParams (const Options& options) + : options(options) + {} + + SearchParams::SearchParams ( + const String& input, + const Options& options + ) : options(options) + { + this->set(input); + } + + SearchParams::SearchParams (const SearchParams& input) + : options(input.options), + data(input.data) + {} + + SearchParams::SearchParams (SearchParams&& input) + : options(std::move(input.options)), + data(std::move(input.data)) + { + input.options = Options {}; + input.data = Entries {}; + } + + SearchParams::SearchParams ( + const Map& input, + const Options& options + ) : options(options) + { + for (const auto& entry : input) { + this->set(entry.first, entry.second); + } + } + + SearchParams::SearchParams ( + const JSON::Object& input, + const Options& options + ) : options(options) + { + for (const auto& entry : input) { + if (entry.second.isString()) { + this->set(entry.first, entry.second.as().data); + } else { + this->set(entry.first, entry.second.str()); + } + } + } + + SearchParams& SearchParams::operator = (const SearchParams& params) { + this->data = params.data; + this->options = params.options; + return *this; + } + + SearchParams& SearchParams::operator = (SearchParams&& params) { + this->data = std::move(params.data); + this->options = std::move(params.options); + params.options = Options {}; + params.data = Entries {}; + return *this; + } + + SearchParams::Value SearchParams::operator [] (const String& key) const { + return this->get(key); + } + + SearchParams::Value& SearchParams::operator [] (const String& key) { + return this->data.at(key); + } + + SearchParams& SearchParams::set (const String& key, const Value& value) { + this->data[key] = value; + return *this; + } + + SearchParams& SearchParams::set (const String& input) { + const auto query = input.starts_with("?") + ? input.substr(1) + : input; + + for (const auto& entry : split(query, '&')) { + const auto parts = split(entry, '='); + if (parts.size() == 2) { + const auto key = trim(parts[0]); + const auto value = trim(parts[1]); + this->set(key, value); + } + } + return *this; + } + + const SearchParams::Value& SearchParams::at (const String& key) const { + return this->data.at(key); + } + + const SearchParams::Value SearchParams::get (const String& key) const { + return this->data.at(key); + } + + const SearchParams::Value SearchParams::get (const String& key) { + return this->data[key]; + } + + bool SearchParams::contains (const String& key) const { + return this->data.contains(key); + } + + SearchParams::Entries::size_type SearchParams::size () const { + return this->data.size(); + } + + const SearchParams::const_iterator SearchParams::begin () const noexcept { + return this->data.begin(); + } + + const SearchParams::const_iterator SearchParams::end () const noexcept { + return this->data.end(); + } + + SearchParams::iterator SearchParams::begin () noexcept { + return this->data.begin(); + } + + SearchParams::iterator SearchParams::end () noexcept { + return this->data.end(); + } + + const String SearchParams::str () const { + Vector components; + for (const auto& entry : *this) { + const auto parts = Vector { + entry.first, + entry.second.str() + }; + + components.push_back(join(parts, "=")); + } + + return join(components, "&"); + } + + const Map SearchParams::map () const { + auto map = Map{}; + for (const auto& entry : *this) { + map[entry.first] = entry.second.str(); + } + return map; + } + + const JSON::Object SearchParams::json () const { + return this->map(); + } +} diff --git a/src/runtime/url/url.cc b/src/runtime/url/url.cc new file mode 100644 index 0000000000..515c881455 --- /dev/null +++ b/src/runtime/url/url.cc @@ -0,0 +1,317 @@ +#include "../url.hh" +#include "../string.hh" + +using namespace ssc::runtime::string; + +namespace ssc::runtime::url { + const URL::Components URL::Components::parse (const String& url) { + URL::Components components; + components.originalURL = url; + auto input = url; + + if (input.starts_with("./")) { + input = input.substr(1); + } + + if (!input.starts_with("/")) { + const auto colon = input.find(':'); + + if (colon != String::npos) { + components.scheme = input.substr(0, colon); + input = input.substr(colon + 1, input.size()); + + if (input.starts_with("//")) { + input = input.substr(2, input.size()); + + const auto slash = input.find("/"); + if (slash != String::npos) { + components.authority = input.substr(0, slash); + input = input.substr(slash, input.size()); + } else { + const auto questionMark = input.find("?"); + const auto fragment = input.find("#"); + if (questionMark != String::npos & fragment != String::npos) { + if (questionMark < fragment) { + components.authority = input.substr(0, questionMark); + input = input.substr(questionMark, input.size()); + } else { + components.authority = input.substr(0, fragment); + input = input.substr(fragment, input.size()); + } + } else if (questionMark != String::npos) { + components.authority = input.substr(0, questionMark); + input = input.substr(questionMark, input.size()); + } else if (fragment != String::npos) { + components.authority = input.substr(0, fragment); + input = input.substr(fragment, input.size()); + } else { + components.authority = input; + components.pathname = "/"; + } + } + } + } + } + + if (components.pathname.size() == 0) { + const auto questionMark = input.find("?"); + const auto fragment = input.find("#"); + + if (questionMark != String::npos && fragment != String::npos) { + if (questionMark < fragment) { + components.pathname = input.substr(0, questionMark); + components.query = input.substr(questionMark + 1, fragment - questionMark - 1); + components.fragment = input.substr(fragment + 1, input.size()); + } else { + components.pathname = input.substr(0, fragment); + components.fragment = input.substr(fragment + 1, input.size()); + } + } else if (questionMark != String::npos) { + components.pathname = input.substr(0, questionMark); + components.query = input.substr(questionMark + 1, input.size()); + } else if (fragment != String::npos) { + components.pathname = input.substr(0, fragment); + components.fragment = input.substr(fragment + 1, input.size()); + } else { + components.pathname = input; + } + + if (!components.pathname.starts_with("/")) { + components.pathname = "/" + components.pathname; + } + } + + return components; + } + + URL::URL (const URL& url) { + this->href = url.href; + this->origin = url.origin; + this->protocol = url.protocol; + this->username = url.username; + this->password = url.password; + this->hostname = url.hostname; + this->port = url.port; + this->pathname = url.pathname; + this->search = url.search; + this->hash = url.hash; + + this->scheme = url.scheme; + this->fragment = url.fragment; + this->query = url.query; + + this->searchParams = url.searchParams; + this->pathComponents = url.pathComponents; + } + + URL::URL (URL&& url) { + this->href = url.href; + this->origin = url.origin; + this->protocol = url.protocol; + this->username = url.username; + this->password = url.password; + this->hostname = url.hostname; + this->port = url.port; + this->pathname = url.pathname; + this->search = url.search; + this->hash = url.hash; + + this->scheme = url.scheme; + this->fragment = url.fragment; + this->query = url.query; + + this->searchParams = std::move(url.searchParams); + this->pathComponents = std::move(url.pathComponents); + + url.href = ""; + url.origin = ""; + url.protocol = ""; + url.username = ""; + url.password = ""; + url.hostname = ""; + url.port = ""; + url.pathname = ""; + url.search = ""; + url.hash = ""; + url.scheme = ""; + url.fragment = ""; + url.query = ""; + + url.searchParams = SearchParams {}; + url.pathComponents = PathComponents {}; + } + + URL::URL (const JSON::Object& json) + : URL( + json.has("href") + ? json.get("href").str() + : "" + ) + {} + + URL::URL (const String& href, bool decodeURIComponents) + : searchParams(SearchParams::Options { decodeURIComponents }) + { + if (href.size() > 0) { + this->set(href, decodeURIComponents); + } + } + + URL& URL::operator = (const URL& url) { + this->href = url.href; + this->origin = url.origin; + this->protocol = url.protocol; + this->username = url.username; + this->password = url.password; + this->hostname = url.hostname; + this->port = url.port; + this->pathname = url.pathname; + this->search = url.search; + this->hash = url.hash; + + this->scheme = url.scheme; + this->fragment = url.fragment; + this->query = url.query; + + this->searchParams = url.searchParams; + this->pathComponents = url.pathComponents; + return *this; + } + + URL& URL::operator = (URL&& url) { + this->href = url.href; + this->origin = url.origin; + this->protocol = url.protocol; + this->username = url.username; + this->password = url.password; + this->hostname = url.hostname; + this->port = url.port; + this->pathname = url.pathname; + this->search = url.search; + this->hash = url.hash; + + this->scheme = url.scheme; + this->fragment = url.fragment; + this->query = url.query; + + this->searchParams = std::move(url.searchParams); + this->pathComponents = std::move(url.pathComponents); + + url.href = ""; + url.origin = ""; + url.protocol = ""; + url.username = ""; + url.password = ""; + url.hostname = ""; + url.port = ""; + url.pathname = ""; + url.search = ""; + url.hash = ""; + url.scheme = ""; + url.fragment = ""; + url.query = ""; + + url.searchParams = SearchParams {}; + url.pathComponents = PathComponents {}; + return *this; + } + + void URL::set (const String& href, bool decodeURIComponents) { + const auto components = URL::Components::parse(href); + + this->scheme = components.scheme; + this->pathname = components.pathname; + this->query = components.query; + this->fragment = components.fragment; + this->search = this->query.size() > 0 ? "?" + this->query : ""; + this->hash = this->fragment.size() > 0 ? "#" + this->fragment : ""; + + if (components.scheme.size() > 0) { + this->protocol = components.scheme + ":"; + } + + const auto authorityParts = components.authority.size() > 0 + ? split(components.authority, '@') + : Vector {}; + + if (authorityParts.size() == 2) { + const auto userParts = split(authorityParts[0], ':'); + + if (userParts.size() == 2) { + this->username = userParts[0]; + this->password = userParts[1]; + } else if (userParts.size() == 1) { + this->username = userParts[0]; + } + + const auto hostParts = split(authorityParts[1], ':'); + if (hostParts.size() > 1) { + this->port = hostParts[1]; + } + + if (hostParts.size() > 0) { + this->hostname = hostParts[0]; + } + } else if (authorityParts.size() == 1) { + const auto hostParts = split(authorityParts[0], ':'); + if (hostParts.size() > 1) { + this->port = hostParts[1]; + } + + if (hostParts.size() > 0) { + this->hostname = hostParts[0]; + } + } + + if (this->protocol.size() > 0) { + if (this->hostname.size() > 0) { + this->origin = this->protocol + "//" + this->hostname; + } else { + this->origin = this->protocol + this->pathname; + } + + this->href = this->origin + this->pathname + this->search + this->hash; + } + + if (this->query.size() > 0) { + for (const auto& entry : split(this->query, '&')) { + const auto parts = split(entry, '='); + if (parts.size() == 2) { + const auto key = trim(parts[0]); + const auto value = trim(parts[1]); + this->searchParams.set(key, value); + } + } + } + + if (this->pathname.size() > 0) { + this->pathComponents.set(this->pathname); + } + } + + const String URL::str () const { + return this->href; + } + + const char* URL::c_str () const { + return this->href.c_str(); + } + + const JSON::Object URL::json () const { + return JSON::Object::Entries { + {"href", this->href}, + {"origin", this->origin}, + {"protocol", this->protocol}, + {"username", this->username}, + {"password", this->password}, + {"hostname", this->hostname}, + {"pathname", this->pathname}, + {"search", this->search}, + {"hash", this->hash} + }; + } + + const size_t URL::size () const { + return this->href.size(); + } +} diff --git a/src/core/version.hh b/src/runtime/version.hh similarity index 74% rename from src/core/version.hh rename to src/runtime/version.hh index 46a64e5f9c..81e7981cf4 100644 --- a/src/core/version.hh +++ b/src/runtime/version.hh @@ -1,13 +1,12 @@ -#ifndef SOCKET_RUNTIME_CORE_VERSION_H -#define SOCKET_RUNTIME_CORE_VERSION_H +#ifndef SOCKET_RUNTIME_VERSION_H +#define SOCKET_RUNTIME_VERSION_H -#include "../platform/string.hh" +#include "string.hh" #include "config.hh" -namespace SSC { +namespace ssc::runtime::version { inline const auto VERSION_FULL_STRING = String(CONVERT_TO_STRING(SOCKET_RUNTIME_VERSION) " (" CONVERT_TO_STRING(SOCKET_RUNTIME_VERSION_HASH) ")"); inline const auto VERSION_HASH_STRING = String(CONVERT_TO_STRING(SOCKET_RUNTIME_VERSION_HASH)); inline const auto VERSION_STRING = String(CONVERT_TO_STRING(SOCKET_RUNTIME_VERSION)); } - #endif diff --git a/src/runtime/webview.hh b/src/runtime/webview.hh new file mode 100644 index 0000000000..32de1729d4 --- /dev/null +++ b/src/runtime/webview.hh @@ -0,0 +1,33 @@ +#ifndef SOCKET_RUNTIME_WEBVIEW_H +#define SOCKET_RUNTIME_WEBVIEW_H + +#include "webview/scheme_handlers.hh" +#include "webview/navigator.hh" +#include "webview/preload.hh" +#include "webview/webview.hh" +#include "webview/client.hh" +#include "ipc.hh" + +namespace ssc::runtime::webview { + class IBridge : public ipc::IBridge { + public: + Map userConfig; + SchemeHandlers schemeHandlers; + Navigator navigator; + + IBridge ( + context::Dispatcher& dispatcher, + context::RuntimeContext& context, + const Client& client, + Map userConfig + ) + : ipc::IBridge(dispatcher, context, client), + schemeHandlers(this), + userConfig(userConfig), + navigator(this) + {} + + virtual bool navigate (const String& url) = 0; + }; +} +#endif diff --git a/src/core/webview.kt b/src/runtime/webview.kt similarity index 100% rename from src/core/webview.kt rename to src/runtime/webview.kt diff --git a/src/runtime/webview/client.cc b/src/runtime/webview/client.cc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/runtime/webview/client.hh b/src/runtime/webview/client.hh new file mode 100644 index 0000000000..b25f3c6cb7 --- /dev/null +++ b/src/runtime/webview/client.hh @@ -0,0 +1,16 @@ +#ifndef SOCKET_RUNTIME_WEBVIEW_CLIENT_H +#define SOCKET_RUNTIME_WEBVIEW_CLIENT_H + +#include "../unique_client.hh" +#include "../ipc.hh" + +namespace ssc::runtime::webview { + struct Client : public UniqueClient { + using UniqueClient::UniqueClient; + + Client (const UniqueClient& client) + : UniqueClient(client) + {} + }; +} +#endif diff --git a/src/ipc/navigator.cc b/src/runtime/webview/navigator.cc similarity index 89% rename from src/ipc/navigator.cc rename to src/runtime/webview/navigator.cc index bf8d784eee..0f33b2cdc8 100644 --- a/src/ipc/navigator.cc +++ b/src/runtime/webview/navigator.cc @@ -1,8 +1,16 @@ -#include "../app/app.hh" -#include "../window/window.hh" +#include "../filesystem.hh" +#include "../runtime.hh" +#include "../config.hh" +#include "../string.hh" -#include "bridge.hh" -#include "navigator.hh" +#include "../webview.hh" + +using ssc::runtime::config::getDevHost; + +using ssc::runtime::string::parseStringList; +using ssc::runtime::string::replace; +using ssc::runtime::string::split; +using ssc::runtime::string::trim; #if SOCKET_RUNTIME_PLATFORM_APPLE @implementation SSCNavigationDelegate @@ -52,8 +60,8 @@ @end #endif -namespace SSC::IPC { - Navigator::Location::Location (Bridge* bridge) +namespace ssc::runtime::webview { + Navigator::Location::Location (IBridge* bridge) : bridge(bridge), URL() {} @@ -99,7 +107,7 @@ namespace SSC::IPC { const auto filename = (fs::path(dirname) / fs::path(result)).make_preferred(); // 1. Try the given path if it's a file - if (FileResource::isFile(filename)) { + if (filesystem::Resource::isFile(filename)) { return Navigator::Location::Resolution { .pathname = "/" + replace(fs::relative(filename, dirname).string(), "\\\\", "/") }; @@ -107,7 +115,7 @@ namespace SSC::IPC { // 2. Try appending a `/` to the path and checking for an index.html const auto index = filename / fs::path("index.html"); - if (FileResource::isFile(index)) { + if (filesystem::Resource::isFile(index)) { if (filename.string().ends_with("\\") || filename.string().ends_with("/")) { return Navigator::Location::Resolution { .pathname = "/" + replace(fs::relative(index, dirname).string(), "\\\\", "/"), @@ -123,7 +131,7 @@ namespace SSC::IPC { // 3. Check if appending a .html file extension gives a valid file const auto html = Path(filename).replace_extension(".html"); - if (FileResource::isFile(html)) { + if (filesystem::Resource::isFile(html)) { return Navigator::Location::Resolution { .pathname = "/" + replace(fs::relative(html, dirname).string(), "\\\\", "/") }; @@ -175,10 +183,9 @@ namespace SSC::IPC { this->bridge->navigate(url); } - Navigator::Navigator (Bridge* bridge) + Navigator::Navigator (IBridge* bridge) : bridge(bridge), - location(bridge), - serviceWorker(App::sharedApplication()->serviceWorkerContainer) + location(bridge) { #if SOCKET_RUNTIME_PLATFORM_APPLE this->navigationDelegate = [SSCNavigationDelegate new]; @@ -217,16 +224,14 @@ namespace SSC::IPC { WebKitPolicyDecisionType decisionType, gpointer userData ) { - auto app = App::sharedApplication(); - auto window = app->windowManager.getWindowForWebView(webview); + const auto navigator = reinterpret_cast(userData); + auto window = navigator->bridge->context.getRuntime()->windowManager.getWindowForWebView(webview); if (!window) { webkit_policy_decision_ignore(decision); return false; } - auto navigator = &window->bridge.navigator; - if (decisionType != WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION) { webkit_policy_decision_use(decision); return true; @@ -243,6 +248,7 @@ namespace SSC::IPC { return false; } + webkit_policy_decision_use(decision); return true; })), this @@ -286,9 +292,10 @@ namespace SSC::IPC { const String& requestedURL ) { auto userConfig = this->bridge->userConfig; - const auto app = App::sharedApplication(); const auto links = parseStringList(userConfig["meta_application_links"], ' '); - const auto window = app->windowManager.getWindowForBridge(this->bridge); + const auto window = this->bridge->context.getRuntime()->windowManager.getWindowForBridge( + reinterpret_cast(this->bridge) + ); const auto applinks = parseStringList(userConfig["meta_application_links"], ' '); const auto currentURLComponents = URL::Components::parse(currentURL); @@ -388,8 +395,6 @@ namespace SSC::IPC { requestedURL.starts_with("node:") || requestedURL.starts_with("npm:") || requestedURL.starts_with("ipc:") || - #if SOCKET_RUNTIME_PLATFORM_APPLE - #endif requestedURL.starts_with(devHost) ) { return true; @@ -399,27 +404,27 @@ namespace SSC::IPC { } void Navigator::configureMounts () { - static const auto wellKnownPaths = FileResource::getWellKnownPaths(); - this->location.mounts = FileResource::getMountedPaths(); + static const auto wellKnownPaths = filesystem::Resource::getWellKnownPaths(); + this->location.mounts = filesystem::Resource::getMountedPaths(); for (const auto& entry : this->location.mounts) { const auto& path = entry.first; #if SOCKET_RUNTIME_PLATFORM_LINUX - auto webContext = webkit_web_context_get_default(); + auto webContext = this->bridge->webContext; if (path != wellKnownPaths.home.string()) { webkit_web_context_add_path_to_sandbox(webContext, path.c_str(), false); } #endif } - #if SOCKET_RUNTIME_PLATFORM_LINUX - auto webContext = webkit_web_context_get_default(); - for (const auto& entry : wellKnownPaths.entries()) { - if (FileResource::isDirectory(entry) && entry != wellKnownPaths.home) { - webkit_web_context_add_path_to_sandbox(webContext, entry.c_str(), false); - } + #if SOCKET_RUNTIME_PLATFORM_LINUX + auto webContext = this->bridge->webContext; + for (const auto& entry : wellKnownPaths.entries()) { + if (filesystem::Resource::isDirectory(entry) && entry != wellKnownPaths.home) { + webkit_web_context_add_path_to_sandbox(webContext, entry.c_str(), false); } - #endif + } + #endif } } diff --git a/src/ipc/navigator.hh b/src/runtime/webview/navigator.hh similarity index 74% rename from src/ipc/navigator.hh rename to src/runtime/webview/navigator.hh index 958bf649cc..3f14c3c81e 100644 --- a/src/ipc/navigator.hh +++ b/src/runtime/webview/navigator.hh @@ -1,21 +1,14 @@ -#ifndef SOCKET_RUNTIME_IPC_NAVIGATOR_H -#define SOCKET_RUNTIME_IPC_NAVIGATOR_H - -#include "../core/config.hh" -#include "../core/resource.hh" -#include "../core/url.hh" -#include "../core/webview.hh" -#include "../serviceworker/container.hh" - -namespace SSC::IPC { - // forward - class Bridge; - class Navigator; -} +#ifndef SOCKET_RUNTIME_WEBVIEW_NAVIGATOR_H +#define SOCKET_RUNTIME_WEBVIEW_NAVIGATOR_H + +#include "../filesystem.hh" +#include "../url.hh" + +#include "webview.hh" #if SOCKET_RUNTIME_PLATFORM_APPLE @interface SSCNavigationDelegate : NSObject -@property (nonatomic) SSC::IPC::Navigator* navigator; +@property (nonatomic) ssc::webview::Navigator* navigator; - (void) webView: (WKWebView*) webView didFailNavigation: (WKNavigation*) navigation withError: (NSError*) error; @@ -34,7 +27,8 @@ namespace SSC::IPC { @end #endif -namespace SSC::IPC { +namespace ssc::runtime::webview { + class IBridge; class Navigator { public: struct Location : public URL { @@ -54,31 +48,31 @@ namespace SSC::IPC { bool isMount () const; }; - Bridge* bridge = nullptr; - Map workers; - Map mounts; + IBridge* bridge = nullptr; + Map workers; + Map mounts; Location () = delete; Location (const Location&) = delete; - Location (Bridge* bridge); + Location (IBridge* bridge); void init (); void assign (const String& url); const Resolution resolve ( const Path& pathname, - const Path& dirname = FileResource::getResourcesPath() + const Path& dirname = filesystem::Resource::getResourcesPath() ); const Resolution resolve ( const String& pathname, - const String& dirname = FileResource::getResourcesPath().string() + const String& dirname = filesystem::Resource::getResourcesPath().string() ); }; - ServiceWorkerContainer& serviceWorker; + //ServiceWorkerContainer& serviceWorker; Location location; - Bridge* bridge = nullptr; + IBridge* bridge = nullptr; #if SOCKET_RUNTIME_PLATFORM_APPLE SSCNavigationDelegate* navigationDelegate = nullptr; @@ -86,7 +80,7 @@ namespace SSC::IPC { Navigator () = delete; Navigator (const Navigator&) = delete; - Navigator (Bridge* bridge); + Navigator (IBridge* bridge); ~Navigator (); void init (); diff --git a/src/ipc/navigator.kt b/src/runtime/webview/navigator.kt similarity index 93% rename from src/ipc/navigator.kt rename to src/runtime/webview/navigator.kt index 6e1a8e64e5..b9123a750c 100644 --- a/src/ipc/navigator.kt +++ b/src/runtime/webview/navigator.kt @@ -1,5 +1,5 @@ // vim: set sw=2: -package socket.runtime.ipc +package socket.runtime.webview open class Navigator (val bridge: Bridge) { fun isNavigationRequestAllowed ( diff --git a/src/runtime/webview/origin.cc b/src/runtime/webview/origin.cc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/runtime/webview/origin.hh b/src/runtime/webview/origin.hh new file mode 100644 index 0000000000..e1ace7f234 --- /dev/null +++ b/src/runtime/webview/origin.hh @@ -0,0 +1,12 @@ +#ifndef SOCKET_RUNTIME_WEBVIEW_ORIGIN_H +#define SOCKET_RUNTIME_WEBVIEW_ORIGIN_H + +#include "../platform.hh" + +namespace ssc::runtime::webview { + struct Origin { + using ID = uint64_t; + String name; + }; +} +#endif diff --git a/src/ipc/preload.cc b/src/runtime/webview/preload.cc similarity index 90% rename from src/ipc/preload.cc rename to src/runtime/webview/preload.cc index 2c80bc1d30..2c0c8b2585 100644 --- a/src/ipc/preload.cc +++ b/src/runtime/webview/preload.cc @@ -1,7 +1,24 @@ -#include "../core/core.hh" -#include "preload.hh" - -namespace SSC::IPC { +#include "../filesystem.hh" +#include "../webview.hh" +#include "../string.hh" +#include "../config.hh" +#include "../http.hh" +#include "../env.hh" +#include "../url.hh" + +using ssc::runtime::config::getUserConfig; +using ssc::runtime::config::getDevHost; +using ssc::runtime::config::getDevPort; + +using ssc::runtime::url::decodeURIComponent; +using ssc::runtime::url::encodeURIComponent; +using ssc::runtime::http::toHeaderCase; +using ssc::runtime::string::replace; +using ssc::runtime::string::join; +using ssc::runtime::string::tmpl; +using ssc::runtime::string::trim; + +namespace ssc::runtime::webview { static constexpr auto DEFAULT_REFERRER_POLICY = "unsafe-url"; static constexpr auto RUNTIME_PRELOAD_META_BEGIN_TAG = ( R"HTML()HTML" @@ -56,10 +73,10 @@ namespace SSC::IPC { void Preload::configure (const Options& options) { this->options = options; - this->headers = options.headers; - this->metadata = options.metadata; + this->headers = this->options.headers; + this->metadata = this->options.metadata; - if (options.features.useHTMLMarkup) { + if (this->options.features.useHTMLMarkup) { if (!this->metadata.contains("referrer")) { this->metadata["referrer"] = DEFAULT_REFERRER_POLICY; } @@ -71,20 +88,20 @@ namespace SSC::IPC { } } - if (Env::has("SOCKET_RUNTIME_VM_DEBUG")) { - this->options.env["SOCKET_RUNTIME_VM_DEBUG"] = Env::get("SOCKET_RUNTIME_VM_DEBUG"); + if (runtime::env::has("SOCKET_RUNTIME_VM_DEBUG")) { + this->options.env["SOCKET_RUNTIME_VM_DEBUG"] = runtime::env::get("SOCKET_RUNTIME_VM_DEBUG"); } - if (Env::has("SOCKET_RUNTIME_NPM_DEBUG")) { - this->options.env["SOCKET_RUNTIME_NPM_DEBUG"] = Env::get("SOCKET_RUNTIME_NPM_DEBUG"); + if (runtime::env::has("SOCKET_RUNTIME_NPM_DEBUG")) { + this->options.env["SOCKET_RUNTIME_NPM_DEBUG"] = runtime::env::get("SOCKET_RUNTIME_NPM_DEBUG"); } - if (Env::has("SOCKET_RUNTIME_SHARED_WORKER_DEBUG")) { - this->options.env["SOCKET_RUNTIME_SHARED_WORKER_DEBUG"] = Env::get("SOCKET_RUNTIME_SHARED_WORKER_DEBUG"); + if (runtime::env::has("SOCKET_RUNTIME_SHARED_WORKER_DEBUG")) { + this->options.env["SOCKET_RUNTIME_SHARED_WORKER_DEBUG"] = runtime::env::get("SOCKET_RUNTIME_SHARED_WORKER_DEBUG"); } - if (Env::has("SOCKET_RUNTIME_SERVICE_WORKER_DEBUG")) { - this->options.env["SOCKET_RUNTIME_SERVICE_WORKER_DEBUG"] = Env::get("SOCKET_RUNTIME_SERVICE_WORKER_DEBUG"); + if (runtime::env::has("SOCKET_RUNTIME_SERVICE_WORKER_DEBUG")) { + this->options.env["SOCKET_RUNTIME_SERVICE_WORKER_DEBUG"] = runtime::env::get("SOCKET_RUNTIME_SERVICE_WORKER_DEBUG"); } } @@ -122,7 +139,7 @@ namespace SSC::IPC { buffers.push_back(tmpl( R"HTML()HTML", - Map { + Map { {"name", trim(entry.first)}, {"content", trim(decodeURIComponent(entry.second))} } @@ -135,7 +152,7 @@ namespace SSC::IPC { for (const auto &entry : this->headers) { buffers.push_back(tmpl( R"HTML()HTML", - Map { + Map { {"header", toHeaderCase(entry.name)}, {"value", trim(decodeURIComponent(entry.value.str()))} } @@ -218,7 +235,7 @@ namespace SSC::IPC { }) } )JAVASCRIPT", - Map { + Map { {"argv", args["argv"].str()}, {"conduit", args["conduit"].str()}, {"debug", args["debug"].str()}, @@ -239,18 +256,11 @@ namespace SSC::IPC { configurable: false, enumerable: true, get: () => { - if ( - globalThis.origin.includes(globalThis.__args.config.meta_bundle_identifier.toLowerCase()) || - globalThis.origin.includes(globalThis.__args.client.host + ':' + globalThis.__args.client.port) - ) { - return globalThis.window && globalThis.top !== globalThis.window - ? '{{id}}' - : globalThis.window && globalThis.top - ? '{{clientId}}' - : null - } - - return null + return globalThis.window && globalThis.top !== globalThis.window + ? '{{id}}' + : globalThis.window && globalThis.top + ? '{{clientId}}' + : null } }, type: { @@ -313,24 +323,17 @@ namespace SSC::IPC { configurable: false, enumerable: true, get: () => { - if ( - globalThis.origin.includes(globalThis.__args.config.meta_bundle_identifier.toLowerCase()) || - globalThis.origin.includes(globalThis.__args.client.host + ':' + globalThis.__args.client.port) - ) { - return globalThis.window && globalThis.top !== globalThis.window - ? 'nested' - : globalThis.window && globalThis.top - ? 'top-level' - : 'none' - } - - return 'none' + return globalThis.window && globalThis.top !== globalThis.window + ? 'nested' + : globalThis.window && globalThis.top + ? 'top-level' + : 'none' } }, }) } )JAVASCRIPT", - Map { + Map { {"id", std::to_string(rand64())}, {"clientId", std::to_string(this->options.client.id)}, {"platform", std::regex_replace(platform.os, platformPattern, "darwin")}, @@ -353,7 +356,7 @@ namespace SSC::IPC { buffers.push_back(tmpl( R"JAVASCRIPT(__RAW_CONFIG__['{{key}}'] = '{{value}}')JAVASCRIPT", - Map {{"key", key}, {"value", encodeURIComponent(value)}} + Map {{"key", key}, {"value", encodeURIComponent(value)}} )); } @@ -406,7 +409,7 @@ namespace SSC::IPC { buffers.push_back(tmpl( R"JAVASCRIPT(__RAW_ENV__['{{key}}'] = '{{value}}')JAVASCRIPT", - Map {{"key", key}, {"value", encodeURIComponent(value)}} + Map {{"key", key}, {"value", encodeURIComponent(value)}} )); } @@ -455,7 +458,7 @@ namespace SSC::IPC { }) } )JAVASCRIPT", - Map {{"pathname", pathname}} + Map {{"pathname", pathname}} )); } } @@ -546,7 +549,7 @@ namespace SSC::IPC { import 'socket:internal/init' {{userScript}} )JAVASCRIPT", - Map {{"userScript", this->options.userScript}} + Map {{"userScript", this->options.userScript}} )); } else { buffers.push_back(";(() => {"); @@ -575,7 +578,7 @@ namespace SSC::IPC { } } )JAVASCRIPT", - Map {{"userScript", this->options.userScript}} + Map {{"userScript", this->options.userScript}} )); buffers.push_back("})();"); } @@ -697,7 +700,7 @@ namespace SSC::IPC { buffers.push_back(tmpl( R"JAVASCRIPT( - if (!globalThis.RUNTIME_PRIMORDIAL_OVERRIDES) { + if (!('__RUNTIME_PRIMORDIAL_OVERRIDES__' in globalThis) { Object.defineProperty(globalThis, '__RUNTIME_PRIMORDIAL_OVERRIDES__', { configurable: false, enumerable: false, @@ -706,7 +709,7 @@ namespace SSC::IPC { }) } )JAVASCRIPT", - Map {{"RUNTIME_PRIMORDIAL_OVERRIDES", this->options.RUNTIME_PRIMORDIAL_OVERRIDES}} + Map {{"RUNTIME_PRIMORDIAL_OVERRIDES", this->options.RUNTIME_PRIMORDIAL_OVERRIDES}} )); if (this->options.features.useHTMLMarkup) { @@ -752,15 +755,14 @@ namespace SSC::IPC { } auto protocolHandlerSchemes = options.protocolHandlerSchemes; - auto userConfig = this->options.userConfig; auto preload = this->str(); auto output = html; if ( html.find(RUNTIME_PRELOAD_IMPORTMAP_BEGIN_TAG) == String::npos && - userConfig.contains("webview_importmap") && - userConfig.at("webview_importmap").size() > 0 + this->options.userConfig.contains("webview_importmap") && + this->options.userConfig.at("webview_importmap").size() > 0 ) { - auto resource = FileResource(Path(userConfig.at("webview_importmap"))); + auto resource = filesystem::Resource(Path(this->options.userConfig.at("webview_importmap"))); if (resource.exists()) { const auto bytes = resource.read(); @@ -769,7 +771,7 @@ namespace SSC::IPC { preload = ( tmpl( R"HTML()HTML", - Map {{"importmap", String(bytes, resource.size())}} + Map {{"importmap", String(bytes, resource.size())}} ) + preload ); } @@ -779,7 +781,7 @@ namespace SSC::IPC { protocolHandlerSchemes.push_back("node:"); protocolHandlerSchemes.push_back("npm:"); - output = tmpl(output, Map { + output = tmpl(output, Map { {"protocol_handlers", join(protocolHandlerSchemes, " ")} }); diff --git a/src/ipc/preload.hh b/src/runtime/webview/preload.hh similarity index 69% rename from src/ipc/preload.hh rename to src/runtime/webview/preload.hh index 1833768c08..2484a5a863 100644 --- a/src/ipc/preload.hh +++ b/src/runtime/webview/preload.hh @@ -1,16 +1,17 @@ -#ifndef SOCKET_RUNTIME_IPC_PRELOAD_H -#define SOCKET_RUNTIME_IPC_PRELOAD_H +#ifndef SOCKET_RUNTIME_WEBVIEW_PRELOAD_H +#define SOCKET_RUNTIME_WEBVIEW_PRELOAD_H -#include "../platform/platform.hh" -#include "../core/unique_client.hh" -#include "../core/options.hh" -#include "../core/headers.hh" -#include "../core/config.hh" +#include "../unique_client.hh" +#include "../options.hh" +#include "../http.hh" + +namespace ssc::runtime::webview { + using Headers = http::Headers; -namespace SSC::IPC { /** * `Preload` is a container for state to compile a "preload script" attached - * to an IPC bridge that is injected into HTML documents loaded into a WebView + * to an IPC bridge that is injected into HTML documents loaded into + * a WebView. */ class Preload { String compiled = ""; @@ -18,18 +19,19 @@ namespace SSC::IPC { /** * `Options` is an input container for configuring `Preload` metadata, - * headers, environment variable values, preload compiler features, and more. + * headers, environment variable values, preload compiler features, + * and more. */ - struct Options : SSC::Options { + struct Options : public ssc::runtime::Options { /** * `Features` is a container for a variety of ways of configuring the - * compiler features for the compiled preload that is "injeced" into HTML - * documents. + * compiler features for the compiled preload that is "injeced" + * into HTML documents. */ struct Features { /** - * If `true`, the feature enables global CommonJS values such as `module`, - * `exports`, `require`, `__filename`, and `__dirname`. + * If `true`, the feature enables global CommonJS values such as + * `module`, `exports`, `require`, `__filename`, and `__dirname`. */ bool useGlobalCommonJS = true; @@ -40,37 +42,39 @@ namespace SSC::IPC { bool useGlobalNodeJS = true; /** - * If `true`, the feature enables the automatic import of a "test script" - * that is specified with the `--test ` command - * line argument. This is useful for running tests + * If `true`, the feature enables the automatic import of a + * "test script" that is specified with the + * `--test ` command line argument. + * This is useful for running tests. */ bool useTestScript = false; /** - * If `true`, the feature enables the compiled preload to use HTML markup - * such as `