@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
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The standalone shell, bundled to `dist/standalone-shell.js` and served at `/app/shell.js`: spends the
|
|
3
|
+
* bootstrap link, renders the app's label and page tabs, routes by hash and mounts the app's client.
|
|
4
|
+
*
|
|
5
|
+
* Routes use the hub's own shape (`?query#<module>/<page>`), so links an app builds work in both shells.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { Schema } from "effect";
|
|
10
|
+
declare const StandaloneShellError_base: Schema.Class<StandaloneShellError, Schema.TaggedStruct<"StandaloneShellError", {
|
|
11
|
+
readonly part: Schema.String;
|
|
12
|
+
}>, import("effect/Cause").YieldableError>;
|
|
13
|
+
/** The page's own markup is missing a part this shell fills. */
|
|
14
|
+
export declare class StandaloneShellError extends StandaloneShellError_base {
|
|
15
|
+
}
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=standalone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standalone.d.ts","sourceRoot":"","sources":["../src/standalone.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAA6C,MAAM,EAAE,MAAM,QAAQ,CAAA;;;;AAK1E,gEAAgE;AAChE,qBAAa,oBAAqB,SAAQ,yBAExC;CAAG"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* The standalone shell, bundled to `dist/standalone-shell.js` and served at `/app/shell.js`: spends the
|
|
11
|
+
* bootstrap link, renders the app's label and page tabs, routes by hash and mounts the app's client.
|
|
12
|
+
*
|
|
13
|
+
* Routes use the hub's own shape (`?query#<module>/<page>`), so links an app builds work in both shells.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
import { Effect, ManagedRuntime, Option, Predicate, Schema } from "effect";
|
|
18
|
+
import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/http";
|
|
19
|
+
import { AppManifest } from "./manifest.js";
|
|
20
|
+
/** The page's own markup is missing a part this shell fills. */
|
|
21
|
+
export class StandaloneShellError extends Schema.TaggedError()("StandaloneShellError", {
|
|
22
|
+
part: Schema.String
|
|
23
|
+
}) {
|
|
24
|
+
}
|
|
25
|
+
const runtime = ManagedRuntime.make(FetchHttpClient.layer);
|
|
26
|
+
const csrfKey = "hub-app-csrf";
|
|
27
|
+
const BootstrapReply = Schema.Struct({ csrfToken: Schema.optionalKey(Schema.String) });
|
|
28
|
+
/** An app's client module, checked before it is mounted: a default export with a contract and a mount. */
|
|
29
|
+
const ClientModule = Schema.Struct({
|
|
30
|
+
default: Schema.Struct({
|
|
31
|
+
contract: Schema.Number,
|
|
32
|
+
mount: Schema.declare((input) => Predicate.isFunction(input))
|
|
33
|
+
})
|
|
34
|
+
});
|
|
35
|
+
const element = (selector) => {
|
|
36
|
+
const found = document.querySelector(selector);
|
|
37
|
+
if (found === null)
|
|
38
|
+
throw new StandaloneShellError({ part: selector });
|
|
39
|
+
return found;
|
|
40
|
+
};
|
|
41
|
+
/** Spend `#bootstrap_token=…` once, keep the CSRF token for this tab, and drop the fragment. */
|
|
42
|
+
const bootstrap = Effect.fn("StandaloneShell.bootstrap")(function* () {
|
|
43
|
+
const code = new URLSearchParams(window.location.hash.slice(1)).get("bootstrap_token");
|
|
44
|
+
if (code === null)
|
|
45
|
+
return;
|
|
46
|
+
window.history.replaceState(null, "", `${window.location.pathname}${window.location.search}`);
|
|
47
|
+
const client = yield* HttpClient.HttpClient;
|
|
48
|
+
const response = yield* client.execute(HttpClientRequest.post(new URL("/auth/bootstrap", window.location.href)).pipe(HttpClientRequest.bearerToken(code))).pipe(Effect.provideService(FetchHttpClient.RequestInit, { credentials: "same-origin", cache: "no-store" }));
|
|
49
|
+
const body = yield* response.json;
|
|
50
|
+
if (response.status !== 200)
|
|
51
|
+
return;
|
|
52
|
+
const reply = Schema.decodeUnknownOption(BootstrapReply)(body);
|
|
53
|
+
if (Option.isSome(reply) && reply.value.csrfToken !== undefined) {
|
|
54
|
+
window.sessionStorage.setItem(csrfKey, reply.value.csrfToken);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
/** The page named by `#<module>/<page>`, or the default page for anything else. */
|
|
58
|
+
const currentPage = (manifest) => {
|
|
59
|
+
const [module, page] = window.location.hash.slice(1).split("/");
|
|
60
|
+
return module === manifest.id && manifest.module.pages.some((entry) => entry.id === page) && page !== undefined
|
|
61
|
+
? page
|
|
62
|
+
: manifest.module.defaultPage;
|
|
63
|
+
};
|
|
64
|
+
const start = async () => {
|
|
65
|
+
const manifestText = element("#hub-app-manifest").textContent ?? "";
|
|
66
|
+
const manifest = Schema.decodeUnknownSync(Schema.fromJsonString(AppManifest))(manifestText);
|
|
67
|
+
await runtime.runPromise(bootstrap().pipe(Effect.catch((error) => Effect.logWarning("Sign-in link not spent", error))));
|
|
68
|
+
const root = element("#hub-app-root");
|
|
69
|
+
const portalRoot = element("#hub-app-portals");
|
|
70
|
+
element("[data-hub-shell=label]").textContent = manifest.module.label;
|
|
71
|
+
const crumb = element("[data-hub-shell=crumb]");
|
|
72
|
+
const slot = element("[data-hub-shell=slot]");
|
|
73
|
+
const pages = element("[data-hub-shell=pages]");
|
|
74
|
+
const tabs = manifest.module.pages.map((page) => {
|
|
75
|
+
const link = document.createElement("a");
|
|
76
|
+
link.href = `#${manifest.id}/${page.id}`;
|
|
77
|
+
link.textContent = page.label;
|
|
78
|
+
pages.append(link);
|
|
79
|
+
return { id: page.id, link };
|
|
80
|
+
});
|
|
81
|
+
// The owner session's cookie is scoped to `/api`.
|
|
82
|
+
const api = (path) => `/api/v1/modules/${manifest.id}${path}`;
|
|
83
|
+
let mounted = null;
|
|
84
|
+
const context = () => {
|
|
85
|
+
const page = currentPage(manifest);
|
|
86
|
+
for (const tab of tabs) {
|
|
87
|
+
if (tab.id === page)
|
|
88
|
+
tab.link.setAttribute("aria-current", "page");
|
|
89
|
+
else
|
|
90
|
+
tab.link.removeAttribute("aria-current");
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
contract: 1,
|
|
94
|
+
mode: "standalone",
|
|
95
|
+
api,
|
|
96
|
+
requestHeaders: () => {
|
|
97
|
+
const csrf = window.sessionStorage.getItem(csrfKey);
|
|
98
|
+
return csrf === null ? {} : { "x-csrf-token": csrf };
|
|
99
|
+
},
|
|
100
|
+
caller: { login: "owner" },
|
|
101
|
+
page,
|
|
102
|
+
search: new URLSearchParams(window.location.search),
|
|
103
|
+
active: true,
|
|
104
|
+
navigate: (href, history) => {
|
|
105
|
+
const target = new URL(href, window.location.href);
|
|
106
|
+
if (history === "push")
|
|
107
|
+
window.history.pushState(null, "", target);
|
|
108
|
+
else
|
|
109
|
+
window.history.replaceState(null, "", target);
|
|
110
|
+
mounted?.update(context());
|
|
111
|
+
},
|
|
112
|
+
closeSelection: (parentHref) => {
|
|
113
|
+
window.history.replaceState(null, "", new URL(parentHref, window.location.href));
|
|
114
|
+
mounted?.update(context());
|
|
115
|
+
},
|
|
116
|
+
setCrumb: (items) => {
|
|
117
|
+
crumb.textContent = items.map((item) => item.label).join(" › ");
|
|
118
|
+
},
|
|
119
|
+
setHeaderSlot: (value) => {
|
|
120
|
+
slot.textContent = value === null ? "" : `${value.label}: ${value.text}`;
|
|
121
|
+
},
|
|
122
|
+
setEmpty: (empty) => {
|
|
123
|
+
root.toggleAttribute("data-empty", empty);
|
|
124
|
+
},
|
|
125
|
+
setBadge: () => { },
|
|
126
|
+
portalRoot
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
const loaded = Schema.decodeUnknownOption(ClientModule)(await import(__rewriteRelativeImportExtension(`/app/assets/${manifest.client.entry}`)));
|
|
130
|
+
if (Option.isNone(loaded) || loaded.value.default.contract !== 1) {
|
|
131
|
+
root.textContent = `${manifest.module.label} can't be shown by this version of its shell.`;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
mounted = loaded.value.default.mount(root, context());
|
|
135
|
+
window.addEventListener("popstate", () => mounted?.update(context()));
|
|
136
|
+
window.addEventListener("hashchange", () => mounted?.update(context()));
|
|
137
|
+
};
|
|
138
|
+
if (document.querySelector("#hub-app-manifest") !== null)
|
|
139
|
+
void start();
|
|
140
|
+
//# sourceMappingURL=standalone.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standalone.js","sourceRoot":"","sources":["../src/standalone.ts"],"names":[],"mappings":"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"}
|
package/dist/wire.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The HTTP surface between hub and app, named once so both sides agree.
|
|
3
|
+
*
|
|
4
|
+
* - Hub → app (over the app's unix socket): `/app/*` paths and module routes, with the `x-hub-*` headers.
|
|
5
|
+
* - App → hub (the hub's loopback listener): `/v1/app-services/*`, authenticated with the per-app token.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { Schema } from "effect";
|
|
10
|
+
export declare const appPaths: {
|
|
11
|
+
manifest: "/app/manifest";
|
|
12
|
+
health: "/app/health";
|
|
13
|
+
needsYou: "/app/needs-you";
|
|
14
|
+
badge: "/app/badge";
|
|
15
|
+
assets: "/app/assets/";
|
|
16
|
+
};
|
|
17
|
+
/** Headers the hub sets on a proxied request and strips from every inbound one. */
|
|
18
|
+
export declare const hubHeaders: {
|
|
19
|
+
caller: string;
|
|
20
|
+
requestId: string;
|
|
21
|
+
grant: string;
|
|
22
|
+
listener: string;
|
|
23
|
+
app: string;
|
|
24
|
+
};
|
|
25
|
+
/** Paths the hub serves to apps on its loopback listener. */
|
|
26
|
+
export declare const appServicePaths: {
|
|
27
|
+
host: string;
|
|
28
|
+
fleetRead: string;
|
|
29
|
+
jobs: string;
|
|
30
|
+
relay: string;
|
|
31
|
+
};
|
|
32
|
+
export declare const HostInfo: Schema.Struct<{
|
|
33
|
+
readonly host: Schema.String;
|
|
34
|
+
readonly isHub: Schema.Boolean;
|
|
35
|
+
readonly hosts: Schema.$Array<Schema.String>;
|
|
36
|
+
}>;
|
|
37
|
+
export interface HostInfo extends Schema.Schema.Type<typeof HostInfo> {
|
|
38
|
+
}
|
|
39
|
+
/** Fleet reads the hub already fans out to its peers; each answers the hub's own JSON for that read. */
|
|
40
|
+
export declare const FleetReadKind: Schema.Literals<readonly ["usage", "limits"]>;
|
|
41
|
+
export type FleetReadKind = typeof FleetReadKind.Type;
|
|
42
|
+
export declare const FleetReadRequest: Schema.Struct<{
|
|
43
|
+
readonly read: Schema.Literals<readonly ["usage", "limits"]>;
|
|
44
|
+
readonly query: Schema.$Record<Schema.String, Schema.String>;
|
|
45
|
+
}>;
|
|
46
|
+
export interface FleetReadRequest extends Schema.Schema.Type<typeof FleetReadRequest> {
|
|
47
|
+
}
|
|
48
|
+
/** A Fleet job submitted for the caller the grant names. The payload is checked by the hub's job schema. */
|
|
49
|
+
export declare const JobSubmission: Schema.Struct<{
|
|
50
|
+
readonly grant: Schema.brand<Schema.String, "HubGrant">;
|
|
51
|
+
readonly payload: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
52
|
+
}>;
|
|
53
|
+
export interface JobSubmission extends Schema.Schema.Type<typeof JobSubmission> {
|
|
54
|
+
}
|
|
55
|
+
export declare const JobAccepted: Schema.Struct<{
|
|
56
|
+
readonly jobId: Schema.String;
|
|
57
|
+
readonly status: Schema.String;
|
|
58
|
+
}>;
|
|
59
|
+
export interface JobAccepted extends Schema.Schema.Type<typeof JobAccepted> {
|
|
60
|
+
}
|
|
61
|
+
/** A Relay post as the app, or as the caller when a grant is given. */
|
|
62
|
+
export declare const RelayPost: Schema.Struct<{
|
|
63
|
+
readonly grant: Schema.NullOr<Schema.brand<Schema.String, "HubGrant">>;
|
|
64
|
+
readonly kind: Schema.Literals<readonly ["status", "blocker"]>;
|
|
65
|
+
readonly text: Schema.String;
|
|
66
|
+
}>;
|
|
67
|
+
export interface RelayPost extends Schema.Schema.Type<typeof RelayPost> {
|
|
68
|
+
}
|
|
69
|
+
/** Why the hub refused an app-service call. */
|
|
70
|
+
export declare const AppServiceRefusal: Schema.Struct<{
|
|
71
|
+
readonly code: Schema.Literals<readonly ["unauthorized", "undeclared", "grant_unknown", "grant_expired", "grant_used", "grant_app_mismatch", "grant_read_only", "rejected", "unavailable"]>;
|
|
72
|
+
}>;
|
|
73
|
+
export interface AppServiceRefusal extends Schema.Schema.Type<typeof AppServiceRefusal> {
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=wire.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../src/wire.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAG/B,eAAO,MAAM,QAAQ;;;;;;CAMmB,CAAA;AAExC,mFAAmF;AACnF,eAAO,MAAM,UAAU;IACrB,MAAM;IACN,SAAS;IACT,KAAK;IACL,QAAQ;IACR,GAAG;CACJ,CAAA;AAED,6DAA6D;AAC7D,eAAO,MAAM,eAAe;IAC1B,IAAI;IACJ,SAAS;IACT,IAAI;IACJ,KAAK;CACN,CAAA;AAED,eAAO,MAAM,QAAQ;;;;EAInB,CAAA;AACF,MAAM,WAAW,QAAS,SAAQ,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,QAAQ,CAAC;CAAG;AAExE,wGAAwG;AACxG,eAAO,MAAM,aAAa,+CAAuC,CAAA;AACjE,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC,IAAI,CAAA;AAErD,eAAO,MAAM,gBAAgB;;;EAG3B,CAAA;AACF,MAAM,WAAW,gBAAiB,SAAQ,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,gBAAgB,CAAC;CAAG;AAExF,4GAA4G;AAC5G,eAAO,MAAM,aAAa;;;EAAwD,CAAA;AAClF,MAAM,WAAW,aAAc,SAAQ,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,aAAa,CAAC;CAAG;AAElF,eAAO,MAAM,WAAW;;;EAAiE,CAAA;AACzF,MAAM,WAAW,WAAY,SAAQ,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,WAAW,CAAC;CAAG;AAE9E,uEAAuE;AACvE,eAAO,MAAM,SAAS;;;;EAIpB,CAAA;AACF,MAAM,WAAW,SAAU,SAAQ,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,SAAS,CAAC;CAAG;AAE1E,+CAA+C;AAC/C,eAAO,MAAM,iBAAiB;;EAY5B,CAAA;AACF,MAAM,WAAW,iBAAkB,SAAQ,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,iBAAiB,CAAC;CAAG"}
|
package/dist/wire.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The HTTP surface between hub and app, named once so both sides agree.
|
|
3
|
+
*
|
|
4
|
+
* - Hub → app (over the app's unix socket): `/app/*` paths and module routes, with the `x-hub-*` headers.
|
|
5
|
+
* - App → hub (the hub's loopback listener): `/v1/app-services/*`, authenticated with the per-app token.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { Schema } from "effect";
|
|
10
|
+
import { Grant } from "./route.js";
|
|
11
|
+
export const appPaths = {
|
|
12
|
+
manifest: "/app/manifest",
|
|
13
|
+
health: "/app/health",
|
|
14
|
+
needsYou: "/app/needs-you",
|
|
15
|
+
badge: "/app/badge",
|
|
16
|
+
assets: "/app/assets/"
|
|
17
|
+
};
|
|
18
|
+
/** Headers the hub sets on a proxied request and strips from every inbound one. */
|
|
19
|
+
export const hubHeaders = {
|
|
20
|
+
caller: "x-hub-caller",
|
|
21
|
+
requestId: "x-hub-request-id",
|
|
22
|
+
grant: "x-hub-grant",
|
|
23
|
+
listener: "x-hub-listener",
|
|
24
|
+
app: "x-hub-app"
|
|
25
|
+
};
|
|
26
|
+
/** Paths the hub serves to apps on its loopback listener. */
|
|
27
|
+
export const appServicePaths = {
|
|
28
|
+
host: "/v1/app-services/host",
|
|
29
|
+
fleetRead: "/v1/app-services/fleet-read",
|
|
30
|
+
jobs: "/v1/app-services/jobs",
|
|
31
|
+
relay: "/v1/app-services/relay"
|
|
32
|
+
};
|
|
33
|
+
export const HostInfo = Schema.Struct({
|
|
34
|
+
host: Schema.String,
|
|
35
|
+
isHub: Schema.Boolean,
|
|
36
|
+
hosts: Schema.Array(Schema.String)
|
|
37
|
+
});
|
|
38
|
+
/** Fleet reads the hub already fans out to its peers; each answers the hub's own JSON for that read. */
|
|
39
|
+
export const FleetReadKind = Schema.Literals(["usage", "limits"]);
|
|
40
|
+
export const FleetReadRequest = Schema.Struct({
|
|
41
|
+
read: FleetReadKind,
|
|
42
|
+
query: Schema.Record(Schema.String, Schema.String)
|
|
43
|
+
});
|
|
44
|
+
/** A Fleet job submitted for the caller the grant names. The payload is checked by the hub's job schema. */
|
|
45
|
+
export const JobSubmission = Schema.Struct({ grant: Grant, payload: Schema.Json });
|
|
46
|
+
export const JobAccepted = Schema.Struct({ jobId: Schema.String, status: Schema.String });
|
|
47
|
+
/** A Relay post as the app, or as the caller when a grant is given. */
|
|
48
|
+
export const RelayPost = Schema.Struct({
|
|
49
|
+
grant: Schema.NullOr(Grant),
|
|
50
|
+
kind: Schema.Literals(["status", "blocker"]),
|
|
51
|
+
text: Schema.String.check(Schema.isNonEmpty(), Schema.isMaxLength(4_000))
|
|
52
|
+
});
|
|
53
|
+
/** Why the hub refused an app-service call. */
|
|
54
|
+
export const AppServiceRefusal = Schema.Struct({
|
|
55
|
+
code: Schema.Literals([
|
|
56
|
+
"unauthorized",
|
|
57
|
+
"undeclared",
|
|
58
|
+
"grant_unknown",
|
|
59
|
+
"grant_expired",
|
|
60
|
+
"grant_used",
|
|
61
|
+
"grant_app_mismatch",
|
|
62
|
+
"grant_read_only",
|
|
63
|
+
"rejected",
|
|
64
|
+
"unavailable"
|
|
65
|
+
])
|
|
66
|
+
});
|
|
67
|
+
//# sourceMappingURL=wire.js.map
|
package/dist/wire.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wire.js","sourceRoot":"","sources":["../src/wire.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAElC,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,QAAQ,EAAE,eAAe;IACzB,MAAM,EAAE,aAAa;IACrB,QAAQ,EAAE,gBAAgB;IAC1B,KAAK,EAAE,YAAY;IACnB,MAAM,EAAE,cAAc;CACgB,CAAA;AAExC,mFAAmF;AACnF,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,MAAM,EAAE,cAAc;IACtB,SAAS,EAAE,kBAAkB;IAC7B,KAAK,EAAE,aAAa;IACpB,QAAQ,EAAE,gBAAgB;IAC1B,GAAG,EAAE,WAAW;CACjB,CAAA;AAED,6DAA6D;AAC7D,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,IAAI,EAAE,uBAAuB;IAC7B,SAAS,EAAE,6BAA6B;IACxC,IAAI,EAAE,uBAAuB;IAC7B,KAAK,EAAE,wBAAwB;CAChC,CAAA;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC;IACpC,IAAI,EAAE,MAAM,CAAC,MAAM;IACnB,KAAK,EAAE,MAAM,CAAC,OAAO;IACrB,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;CACnC,CAAC,CAAA;AAGF,wGAAwG;AACxG,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAA;AAGjE,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,CAAC;IAC5C,IAAI,EAAE,aAAa;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;CACnD,CAAC,CAAA;AAGF,4GAA4G;AAC5G,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;AAGlF,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;AAGzF,uEAAuE;AACvE,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC;IACrC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC5C,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;CAC1E,CAAC,CAAA;AAGF,+CAA+C;AAC/C,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC;IAC7C,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC;QACpB,cAAc;QACd,YAAY;QACZ,eAAe;QACf,eAAe;QACf,YAAY;QACZ,oBAAoB;QACpB,iBAAiB;QACjB,UAAU;QACV,aAAa;KACd,CAAC;CACH,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,65 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knpkv/hub-app",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Contract and runner for apps that run standalone on their own port and mount in the Relay hub",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/knpkv/npm.git",
|
|
9
|
+
"directory": "packages/hub-app"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"sideEffects": [
|
|
13
|
+
"./dist/standalone.js",
|
|
14
|
+
"./dist/standalone-shell.js"
|
|
15
|
+
],
|
|
16
|
+
"main": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./node": {
|
|
24
|
+
"types": "./dist/node.d.ts",
|
|
25
|
+
"import": "./dist/node.js"
|
|
26
|
+
},
|
|
27
|
+
"./hub-client": {
|
|
28
|
+
"types": "./dist/hub-client.d.ts",
|
|
29
|
+
"import": "./dist/hub-client.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"README.md"
|
|
35
|
+
],
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=26"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@effect/platform-node": "4.0.0",
|
|
44
|
+
"@effect/platform-node-shared": "4.0.0",
|
|
45
|
+
"@knpkv/browser-pairing": "^0.8.0"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"effect": "4.0.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@effect/vitest": "4.0.0",
|
|
52
|
+
"@types/node": "^26.6.4",
|
|
53
|
+
"effect": "4.0.0",
|
|
54
|
+
"esbuild": "0.28.2",
|
|
55
|
+
"rimraf": "^6.1.3",
|
|
56
|
+
"typescript": "npm:@typescript/typescript6@^6.0.2",
|
|
57
|
+
"vitest": "^5.0.5"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "tsc -b tsconfig.build.json --force && node scripts/build-shell.mjs",
|
|
61
|
+
"check": "tsc -b tsconfig.json && tsc -p tsconfig.test.json --noEmit",
|
|
62
|
+
"lint": "eslint src test",
|
|
63
|
+
"test": "vitest run"
|
|
64
|
+
}
|
|
6
65
|
}
|