@knpkv/hub-app 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +54 -2
- package/dist/client.d.ts +67 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +17 -0
- package/dist/client.js.map +1 -0
- package/dist/hub-client.d.ts +52 -0
- package/dist/hub-client.d.ts.map +1 -0
- package/dist/hub-client.js +97 -0
- package/dist/hub-client.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +4 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +29 -0
- package/dist/main.js.map +1 -0
- package/dist/manifest.d.ts +267 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +107 -0
- package/dist/manifest.js.map +1 -0
- package/dist/module.d.ts +38 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +70 -0
- package/dist/module.js.map +1 -0
- package/dist/node.d.ts +50 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +253 -0
- package/dist/node.js.map +1 -0
- package/dist/route.d.ts +91 -0
- package/dist/route.d.ts.map +1 -0
- package/dist/route.js +88 -0
- package/dist/route.js.map +1 -0
- package/dist/standalone-page.d.ts +3 -0
- package/dist/standalone-page.d.ts.map +1 -0
- package/dist/standalone-page.js +28 -0
- package/dist/standalone-page.js.map +1 -0
- package/dist/standalone-shell.js +27 -0
- package/dist/standalone.d.ts +17 -0
- package/dist/standalone.d.ts.map +1 -0
- package/dist/standalone.js +140 -0
- package/dist/standalone.js.map +1 -0
- package/dist/wire.d.ts +75 -0
- package/dist/wire.d.ts.map +1 -0
- package/dist/wire.js +67 -0
- package/dist/wire.js.map +1 -0
- package/package.json +62 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 knpkv
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,55 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @knpkv/hub-app
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The contract and runner for apps that run on their own and mount in the Relay hub, from one entry point.
|
|
4
|
+
|
|
5
|
+
An app is always its own process:
|
|
6
|
+
|
|
7
|
+
- **standalone**: `my-app [serve] [--port <n>] [--no-open]` listens on loopback, prints a one-time sign-in link and
|
|
8
|
+
serves a minimal shell at `/`.
|
|
9
|
+
- **attached**: `my-app --socket <path>` listens on an owner-only unix socket. The hub authenticates each browser
|
|
10
|
+
request, proxies it there with the caller in `x-hub-caller` and a grant in `x-hub-grant`, and mounts the app's
|
|
11
|
+
client in its own shell. `HUB_URL` and `HUB_APP_TOKEN_FILE` point the app at the hub's services.
|
|
12
|
+
|
|
13
|
+
## Write an app
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { ModuleHandlers, moduleReadRoute } from "@knpkv/hub-app"
|
|
17
|
+
import { runApp } from "@knpkv/hub-app/node"
|
|
18
|
+
import { Effect, Layer, Schema } from "effect"
|
|
19
|
+
|
|
20
|
+
const handlers = Layer.succeed(ModuleHandlers, {
|
|
21
|
+
routes: [
|
|
22
|
+
moduleReadRoute("/hello", Schema.fromURLSearchParams(Schema.Struct({})), Schema.String, (_query, caller) =>
|
|
23
|
+
Effect.succeed(`hello ${caller.login}`)
|
|
24
|
+
)
|
|
25
|
+
]
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
runApp({ manifest, handlers, clientDirectory: new URL("../client", import.meta.url).pathname }, Layer.empty)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`manifest` is an `AppManifestInput`: `contract: 1`, the app's `id`, its `module` descriptor (label, pages,
|
|
32
|
+
listeners), the shell `services` it uses, its `nav` placement and its standalone `defaultPort`.
|
|
33
|
+
|
|
34
|
+
The client directory holds `client.js` (an ES module whose default export is an `AppClient`) and, optionally,
|
|
35
|
+
`client.css` scoped under `[data-app="<id>"]`. `mount(root, context)` renders into `root` with the app's own
|
|
36
|
+
React (or none); `ShellContext` gives it routing, the path and headers for its module routes, the crumb, a header slot and a
|
|
37
|
+
portal root.
|
|
38
|
+
|
|
39
|
+
## Shell services
|
|
40
|
+
|
|
41
|
+
`ShellServices` (from `@knpkv/hub-app/hub-client`) is available to every handler:
|
|
42
|
+
|
|
43
|
+
- `host`: this host, whether it is the hub, and the fleet's hosts.
|
|
44
|
+
- `fleetRead(read, query)`: the hub's fleet-wide reads.
|
|
45
|
+
- `submitJob(payload)`: a Fleet job for the caller of the request being served. The hub resolves the caller from
|
|
46
|
+
the request's grant, which is spent on use.
|
|
47
|
+
- `relayPost(kind, text)`: a Relay post as that caller, or as the app outside a request.
|
|
48
|
+
|
|
49
|
+
Standalone, every service but `host` fails with `ShellServiceUnavailable("standalone")`; a service the manifest
|
|
50
|
+
doesn't declare fails with `undeclared`.
|
|
51
|
+
|
|
52
|
+
## Versioning
|
|
53
|
+
|
|
54
|
+
`contract` is one integer for the whole shell–app surface. The hub mounts the current contract and the one before
|
|
55
|
+
it; an app on any other is listed as unavailable ("needs a different version of the hub") and never mounted.
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The browser half of an app: its client module default-exports an {@link AppClient}, and the shell
|
|
3
|
+
* (the hub's superapp or the standalone shell) mounts it into an element it owns.
|
|
4
|
+
*
|
|
5
|
+
* **Mental model**
|
|
6
|
+
*
|
|
7
|
+
* - The app owns its React root (or none): nothing but this context crosses between shell and app, so
|
|
8
|
+
* the app's React, rly and effect versions are its own business.
|
|
9
|
+
* - The shell owns history, header, crumb, badge and portals. The app asks for them through the context.
|
|
10
|
+
* - Styling: rly tokens arrive as CSS custom properties from the shell; app CSS is scoped under
|
|
11
|
+
* `[data-app="<id>"]`, which the shell sets on the mount root.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
export interface ShellCrumb {
|
|
16
|
+
readonly label: string;
|
|
17
|
+
readonly href: string;
|
|
18
|
+
}
|
|
19
|
+
/** A small reading the app shows in the shell header, such as "3 hosts". */
|
|
20
|
+
export interface ShellHeaderSlot {
|
|
21
|
+
readonly label: string;
|
|
22
|
+
readonly text: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ShellBadge {
|
|
25
|
+
readonly text: string;
|
|
26
|
+
readonly label: string;
|
|
27
|
+
}
|
|
28
|
+
export interface ShellContext {
|
|
29
|
+
readonly contract: 1;
|
|
30
|
+
readonly mode: "hub" | "standalone";
|
|
31
|
+
/** Absolute path for a module route: `/apps/<id>/v1/modules/<id>/<path>` in the hub, `/api/v1/modules/<id>/<path>` standalone. */
|
|
32
|
+
readonly api: (path: `/${string}`) => string;
|
|
33
|
+
/** Headers the app adds to its own module-route requests: the CSRF token standalone, none in the hub. */
|
|
34
|
+
readonly requestHeaders: () => Readonly<Record<string, string>>;
|
|
35
|
+
readonly caller: {
|
|
36
|
+
readonly login: string;
|
|
37
|
+
};
|
|
38
|
+
readonly page: string;
|
|
39
|
+
readonly search: URLSearchParams;
|
|
40
|
+
/** False while the app's section is hidden; gate portals and focus traps on it. */
|
|
41
|
+
readonly active: boolean;
|
|
42
|
+
readonly navigate: (href: string, history: "push" | "replace", focus?: "page" | "search" | null) => void;
|
|
43
|
+
readonly closeSelection: (parentHref: string) => void;
|
|
44
|
+
/** Items after `Relay › App › Page`; an empty list clears them. */
|
|
45
|
+
readonly setCrumb: (items: ReadonlyArray<ShellCrumb>) => void;
|
|
46
|
+
readonly setHeaderSlot: (slot: ShellHeaderSlot | null) => void;
|
|
47
|
+
/** The app has nothing to show; the shell switches to its empty-state chrome. */
|
|
48
|
+
readonly setEmpty: (empty: boolean) => void;
|
|
49
|
+
readonly setBadge: (badge: ShellBadge | null) => void;
|
|
50
|
+
/** Shell-owned, theme-scoped container for the app's dialogs and popovers. */
|
|
51
|
+
readonly portalRoot: HTMLElement;
|
|
52
|
+
/** Hub only. */
|
|
53
|
+
readonly openHostAccess?: () => void;
|
|
54
|
+
}
|
|
55
|
+
export interface MountedApp {
|
|
56
|
+
/** Called on every navigation or activity change while mounted. */
|
|
57
|
+
readonly update: (context: ShellContext) => void;
|
|
58
|
+
readonly unmount: () => void;
|
|
59
|
+
}
|
|
60
|
+
/** The default export of an app's client entry. A `contract` the shell doesn't support is not mounted. */
|
|
61
|
+
export interface AppClient {
|
|
62
|
+
readonly contract: number;
|
|
63
|
+
readonly mount: (root: HTMLElement, context: ShellContext) => MountedApp;
|
|
64
|
+
}
|
|
65
|
+
/** Typed identity for an app's default export. */
|
|
66
|
+
export declare const defineAppClient: (client: AppClient) => AppClient;
|
|
67
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAA;IACpB,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,YAAY,CAAA;IACnC,kIAAkI;IAClI,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,MAAM,EAAE,KAAK,MAAM,CAAA;IAC5C,yGAAyG;IACzG,QAAQ,CAAC,cAAc,EAAE,MAAM,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAC/D,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAA;IAChC,mFAAmF;IACnF,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAA;IACxG,QAAQ,CAAC,cAAc,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAA;IACrD,mEAAmE;IACnE,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,UAAU,CAAC,KAAK,IAAI,CAAA;IAC7D,QAAQ,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,KAAK,IAAI,CAAA;IAC9D,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAC3C,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAA;IACrD,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAA;IAChC,gBAAgB;IAChB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAA;CACrC;AAED,MAAM,WAAW,UAAU;IACzB,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAA;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAC7B;AAED,0GAA0G;AAC1G,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,KAAK,UAAU,CAAA;CACzE;AAED,kDAAkD;AAClD,eAAO,MAAM,eAAe,WAAY,SAAS,KAAG,SAAmB,CAAA"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The browser half of an app: its client module default-exports an {@link AppClient}, and the shell
|
|
3
|
+
* (the hub's superapp or the standalone shell) mounts it into an element it owns.
|
|
4
|
+
*
|
|
5
|
+
* **Mental model**
|
|
6
|
+
*
|
|
7
|
+
* - The app owns its React root (or none): nothing but this context crosses between shell and app, so
|
|
8
|
+
* the app's React, rly and effect versions are its own business.
|
|
9
|
+
* - The shell owns history, header, crumb, badge and portals. The app asks for them through the context.
|
|
10
|
+
* - Styling: rly tokens arrive as CSS custom properties from the shell; app CSS is scoped under
|
|
11
|
+
* `[data-app="<id>"]`, which the shell sets on the mount root.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
/** Typed identity for an app's default export. */
|
|
16
|
+
export const defineAppClient = (client) => client;
|
|
17
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAwDH,kDAAkD;AAClD,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAiB,EAAa,EAAE,CAAC,MAAM,CAAA"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell services an app server calls: host facts, the hub's fleet reads, Fleet jobs and Relay posts.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - Attached, each call is an HTTP request to the hub's loopback listener with the app's own token.
|
|
7
|
+
* Calls that act for a user carry the grant of the request being served ({@link CurrentGrant}); the hub
|
|
8
|
+
* resolves the caller from its own record, so an app can't claim a caller.
|
|
9
|
+
* - Standalone, `host` answers locally and the rest fail `ShellServiceUnavailable("standalone")`; the app
|
|
10
|
+
* hides what they gate.
|
|
11
|
+
* - A service the manifest doesn't declare fails `undeclared` before any request.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import { Context, Effect, FileSystem, Layer, Schema } from "effect";
|
|
16
|
+
import { HttpClient } from "effect/http";
|
|
17
|
+
import { type AppId, GrantInvalid, type ShellService, ShellServiceUnavailable } from "./manifest.js";
|
|
18
|
+
import { type FleetReadKind, HostInfo, JobAccepted } from "./wire.js";
|
|
19
|
+
declare const ShellServiceRefused_base: Schema.Class<ShellServiceRefused, Schema.TaggedStruct<"ShellServiceRefused", {
|
|
20
|
+
readonly service: Schema.String;
|
|
21
|
+
readonly code: Schema.Literals<readonly ["rejected", "unavailable"]>;
|
|
22
|
+
}>, import("effect/Cause").YieldableError>;
|
|
23
|
+
/** The hub took the request and refused it: a job its policy rejected, or a read it couldn't make. */
|
|
24
|
+
export declare class ShellServiceRefused extends ShellServiceRefused_base {
|
|
25
|
+
}
|
|
26
|
+
export interface ShellServiceCalls {
|
|
27
|
+
readonly host: Effect.Effect<HostInfo, ShellServiceUnavailable>;
|
|
28
|
+
/** The hub's own JSON for the read; decode it with the read's schema. */
|
|
29
|
+
readonly fleetRead: (read: FleetReadKind, query: Readonly<Record<string, string>>) => Effect.Effect<Schema.Json, ShellServiceUnavailable | ShellServiceRefused>;
|
|
30
|
+
/** Submit a Fleet job for the caller of the request being served. */
|
|
31
|
+
readonly submitJob: (payload: Schema.Json) => Effect.Effect<JobAccepted, ShellServiceUnavailable | ShellServiceRefused | GrantInvalid>;
|
|
32
|
+
/** Post as the caller of the request being served, or as the app outside one. */
|
|
33
|
+
readonly relayPost: (kind: "status" | "blocker", text: string) => Effect.Effect<void, ShellServiceUnavailable | ShellServiceRefused | GrantInvalid>;
|
|
34
|
+
}
|
|
35
|
+
declare const ShellServices_base: Context.ServiceClass<ShellServices, "@knpkv/hub-app/ShellServices", ShellServiceCalls>;
|
|
36
|
+
export declare class ShellServices extends ShellServices_base {
|
|
37
|
+
}
|
|
38
|
+
/** Standalone: no hub. `host` is supplied by the runner from this machine. */
|
|
39
|
+
export declare const standaloneShellServices: (host: HostInfo) => ShellServiceCalls;
|
|
40
|
+
export interface AttachedOptions {
|
|
41
|
+
readonly app: AppId;
|
|
42
|
+
readonly hubUrl: string;
|
|
43
|
+
/** Read on every call: the hub issues a fresh token each time it starts, and the app outlives that. */
|
|
44
|
+
readonly tokenFile: string;
|
|
45
|
+
readonly declared: ReadonlyArray<ShellService>;
|
|
46
|
+
}
|
|
47
|
+
/** Attached: every call goes to the hub's loopback listener with this app's current token. */
|
|
48
|
+
export declare const attachedShellServices: (options: AttachedOptions) => Effect.Effect<ShellServiceCalls, never, FileSystem.FileSystem | HttpClient.HttpClient>;
|
|
49
|
+
export declare const attachedLayer: (options: AttachedOptions) => Layer.Layer<ShellServices, never, FileSystem.FileSystem | HttpClient.HttpClient>;
|
|
50
|
+
export declare const standaloneLayer: (host: HostInfo) => Layer.Layer<ShellServices, never, never>;
|
|
51
|
+
export {};
|
|
52
|
+
//# sourceMappingURL=hub-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub-client.d.ts","sourceRoot":"","sources":["../src/hub-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACnE,OAAO,EAAE,UAAU,EAA8C,MAAM,aAAa,CAAA;AACpF,OAAO,EAAE,KAAK,KAAK,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AAEpG,OAAO,EAAsC,KAAK,aAAa,EAAE,QAAQ,EAAc,WAAW,EAAE,MAAM,WAAW,CAAA;;;;;AAErH,sGAAsG;AACtG,qBAAa,mBAAoB,SAAQ,wBAGvC;CAAG;AAEL,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,uBAAuB,CAAC,CAAA;IAC/D,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,CAClB,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,KACpC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,uBAAuB,GAAG,mBAAmB,CAAC,CAAA;IAC9E,qEAAqE;IACrE,QAAQ,CAAC,SAAS,EAAE,CAClB,OAAO,EAAE,MAAM,CAAC,IAAI,KACjB,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,uBAAuB,GAAG,mBAAmB,GAAG,YAAY,CAAC,CAAA;IAC7F,iFAAiF;IACjF,QAAQ,CAAC,SAAS,EAAE,CAClB,IAAI,EAAE,QAAQ,GAAG,SAAS,EAC1B,IAAI,EAAE,MAAM,KACT,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,uBAAuB,GAAG,mBAAmB,GAAG,YAAY,CAAC,CAAA;CACvF;;AAED,qBAAa,aAAc,SAAQ,kBAElC;CAAG;AAEJ,8EAA8E;AAC9E,eAAO,MAAM,uBAAuB,SAAU,QAAQ,KAAG,iBAQxD,CAAA;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAA;IACnB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,uGAAuG;IACvG,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,CAAA;CAC/C;AAED,8FAA8F;AAC9F,eAAO,MAAM,qBAAqB,sHAgGhC,CAAA;AAEF,eAAO,MAAM,aAAa,YAAa,eAAe,qFAAgE,CAAA;AAEtH,eAAO,MAAM,eAAe,SAAU,QAAQ,6CAAgE,CAAA"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell services an app server calls: host facts, the hub's fleet reads, Fleet jobs and Relay posts.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - Attached, each call is an HTTP request to the hub's loopback listener with the app's own token.
|
|
7
|
+
* Calls that act for a user carry the grant of the request being served ({@link CurrentGrant}); the hub
|
|
8
|
+
* resolves the caller from its own record, so an app can't claim a caller.
|
|
9
|
+
* - Standalone, `host` answers locally and the rest fail `ShellServiceUnavailable("standalone")`; the app
|
|
10
|
+
* hides what they gate.
|
|
11
|
+
* - A service the manifest doesn't declare fails `undeclared` before any request.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import { Context, Effect, FileSystem, Layer, Schema } from "effect";
|
|
16
|
+
import { HttpClient, HttpClientRequest } from "effect/http";
|
|
17
|
+
import { GrantInvalid, ShellServiceUnavailable } from "./manifest.js";
|
|
18
|
+
import { CurrentGrant } from "./route.js";
|
|
19
|
+
import { appServicePaths, AppServiceRefusal, HostInfo, hubHeaders, JobAccepted } from "./wire.js";
|
|
20
|
+
/** The hub took the request and refused it: a job its policy rejected, or a read it couldn't make. */
|
|
21
|
+
export class ShellServiceRefused extends Schema.TaggedError()("ShellServiceRefused", {
|
|
22
|
+
service: Schema.String,
|
|
23
|
+
code: Schema.Literals(["rejected", "unavailable"])
|
|
24
|
+
}) {
|
|
25
|
+
}
|
|
26
|
+
export class ShellServices extends Context.Service()("@knpkv/hub-app/ShellServices") {
|
|
27
|
+
}
|
|
28
|
+
/** Standalone: no hub. `host` is supplied by the runner from this machine. */
|
|
29
|
+
export const standaloneShellServices = (host) => {
|
|
30
|
+
const absent = (service) => Effect.fail(new ShellServiceUnavailable({ service, reason: "standalone" }));
|
|
31
|
+
return {
|
|
32
|
+
host: Effect.succeed(host),
|
|
33
|
+
fleetRead: () => absent("fleet.read"),
|
|
34
|
+
submitJob: () => absent("jobs.submit"),
|
|
35
|
+
relayPost: () => absent("relay.post")
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
/** Attached: every call goes to the hub's loopback listener with this app's current token. */
|
|
39
|
+
export const attachedShellServices = Effect.fn("HubApp.attachedShellServices")(function* (options) {
|
|
40
|
+
const client = yield* HttpClient.HttpClient;
|
|
41
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
42
|
+
const url = (path) => new URL(path, options.hubUrl).href;
|
|
43
|
+
const authorized = (request) => fileSystem.readFileString(options.tokenFile).pipe(Effect.map((token) => request.pipe(HttpClientRequest.bearerToken(token.trim()), HttpClientRequest.setHeader(hubHeaders.app, options.app))));
|
|
44
|
+
const declare = (service) => options.declared.includes(service)
|
|
45
|
+
? Effect.void
|
|
46
|
+
: Effect.fail(new ShellServiceUnavailable({ service, reason: "undeclared" }));
|
|
47
|
+
const hubDown = (service) => () => new ShellServiceUnavailable({ service, reason: "hub_down" });
|
|
48
|
+
/** One request; a refusal body maps to its typed failure, anything else non-2xx is the hub being down. */
|
|
49
|
+
const send = (service, request, success) => Effect.gen(function* () {
|
|
50
|
+
yield* declare(service);
|
|
51
|
+
const response = yield* request.pipe(Effect.flatMap(authorized), Effect.flatMap((built) => client.execute(built)), Effect.mapError(hubDown(service)));
|
|
52
|
+
const body = yield* response.json.pipe(Effect.mapError(hubDown(service)));
|
|
53
|
+
if (response.status >= 200 && response.status < 300) {
|
|
54
|
+
return yield* Schema.decodeUnknownEffect(success)(body).pipe(Effect.mapError(hubDown(service)));
|
|
55
|
+
}
|
|
56
|
+
const refusal = yield* Schema.decodeUnknownEffect(AppServiceRefusal)(body).pipe(Effect.mapError(hubDown(service)));
|
|
57
|
+
switch (refusal.code) {
|
|
58
|
+
case "grant_unknown":
|
|
59
|
+
return yield* new GrantInvalid({ reason: "unknown" });
|
|
60
|
+
case "grant_expired":
|
|
61
|
+
return yield* new GrantInvalid({ reason: "expired" });
|
|
62
|
+
case "grant_used":
|
|
63
|
+
return yield* new GrantInvalid({ reason: "used" });
|
|
64
|
+
case "grant_app_mismatch":
|
|
65
|
+
return yield* new GrantInvalid({ reason: "app_mismatch" });
|
|
66
|
+
case "grant_read_only":
|
|
67
|
+
return yield* new GrantInvalid({ reason: "read_only" });
|
|
68
|
+
case "undeclared":
|
|
69
|
+
return yield* new ShellServiceUnavailable({ service, reason: "undeclared" });
|
|
70
|
+
case "rejected":
|
|
71
|
+
case "unavailable":
|
|
72
|
+
return yield* new ShellServiceRefused({ service, code: refusal.code });
|
|
73
|
+
case "unauthorized":
|
|
74
|
+
return yield* hubDown(service)();
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
/** The grant of the request being served; outside one there is no caller to act for. */
|
|
78
|
+
const requireGrant = Effect.gen(function* () {
|
|
79
|
+
const grant = yield* CurrentGrant;
|
|
80
|
+
return grant === null ? yield* new GrantInvalid({ reason: "unknown" }) : grant;
|
|
81
|
+
});
|
|
82
|
+
return ShellServices.of({
|
|
83
|
+
host: send("host", Effect.succeed(HttpClientRequest.get(url(appServicePaths.host))), HostInfo).pipe(Effect.catchTags({
|
|
84
|
+
ShellServiceRefused: () => Effect.fail(hubDown("host")()),
|
|
85
|
+
GrantInvalid: () => Effect.fail(hubDown("host")())
|
|
86
|
+
})),
|
|
87
|
+
fleetRead: (read, query) => send("fleet.read", HttpClientRequest.bodyJson(HttpClientRequest.post(url(appServicePaths.fleetRead)), { read, query }), Schema.Json).pipe(Effect.catchTag("GrantInvalid", () => Effect.fail(hubDown("fleet.read")()))),
|
|
88
|
+
submitJob: (payload) => requireGrant.pipe(Effect.flatMap((grant) => send("jobs.submit", HttpClientRequest.bodyJson(HttpClientRequest.post(url(appServicePaths.jobs)), { grant, payload }), JobAccepted))),
|
|
89
|
+
relayPost: (kind, text) => Effect.gen(function* () {
|
|
90
|
+
const grant = yield* CurrentGrant;
|
|
91
|
+
yield* send("relay.post", HttpClientRequest.bodyJson(HttpClientRequest.post(url(appServicePaths.relay)), { grant, kind, text }), Schema.Struct({}));
|
|
92
|
+
})
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
export const attachedLayer = (options) => Layer.effect(ShellServices, attachedShellServices(options));
|
|
96
|
+
export const standaloneLayer = (host) => Layer.succeed(ShellServices, standaloneShellServices(host));
|
|
97
|
+
//# sourceMappingURL=hub-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub-client.js","sourceRoot":"","sources":["../src/hub-client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA"}
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AASA,OAAO,EAAU,KAAK,EAAU,MAAM,QAAQ,CAAA;AAG9C,OAAO,EAAE,KAAK,SAAS,EAAyB,MAAM,WAAW,CAAA;AAEjE,eAAO,MAAM,MAAM,GAAI,CAAC,EAAE,CAAC,OAAO,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,YAAY,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAG,IAsB7E,CAAA"}
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The process boundary of every app: `runApp(app, platform)` reads argv, environment and the terminal,
|
|
3
|
+
* then serves until SIGINT or SIGTERM. `platform` supplies whatever the app's handlers need beyond
|
|
4
|
+
* `ShellServices`. Call it from the app's `bin` entry.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import { NodeRuntime, NodeServices } from "@effect/platform-node";
|
|
9
|
+
import { browserLaunch, BrowserOpener } from "@knpkv/browser-pairing/launch";
|
|
10
|
+
import { Effect, Layer, Result } from "effect";
|
|
11
|
+
import { FetchHttpClient } from "effect/http";
|
|
12
|
+
import process from "node:process";
|
|
13
|
+
import { readRunMode, serveApp } from "./node.js";
|
|
14
|
+
export const runApp = (app, platform) => {
|
|
15
|
+
const program = Effect.gen(function* () {
|
|
16
|
+
const mode = readRunMode(app.manifest, process.argv.slice(2), process.env);
|
|
17
|
+
if (Result.isFailure(mode))
|
|
18
|
+
return yield* mode.failure;
|
|
19
|
+
const launch = browserLaunch({
|
|
20
|
+
noOpen: mode.success._tag === "Attached" || !mode.success.open,
|
|
21
|
+
browser: process.env.BROWSER,
|
|
22
|
+
ci: process.env.CI,
|
|
23
|
+
interactive: process.stdout.isTTY === true
|
|
24
|
+
});
|
|
25
|
+
return yield* serveApp(app, mode.success, launch);
|
|
26
|
+
});
|
|
27
|
+
NodeRuntime.runMain(program.pipe(Effect.scoped, Effect.provide(Layer.mergeAll(platform, FetchHttpClient.layer, BrowserOpener.layer).pipe(Layer.provideMerge(NodeServices.layer)))));
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACjE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAC5E,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,OAAO,MAAM,cAAc,CAAA;AAClC,OAAO,EAAkB,WAAW,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAEjE,MAAM,CAAC,MAAM,MAAM,GAAG,CAAO,GAAoB,EAAE,QAAwB,EAAQ,EAAE;IACnF,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAClC,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAA;QAC1E,IAAI,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAA;QACtD,MAAM,MAAM,GAAG,aAAa,CAAC;YAC3B,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI;YAC9D,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,OAAO;YAC5B,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE;YAClB,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI;SAC3C,CAAC,CAAA;QACF,OAAO,KAAK,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IACnD,CAAC,CAAC,CAAA;IACF,WAAW,CAAC,OAAO,CACjB,OAAO,CAAC,IAAI,CACV,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,OAAO,CACZ,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,IAAI,CACvE,KAAK,CAAC,YAAY,CAAC,YAAY,CAAC,KAAK,CAAC,CACvC,CACF,CACF,CACF,CAAA;AACH,CAAC,CAAA"}
|