@kingsleyweb/bun-ui 0.0.0-stage → 3.0.0-rc.2
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 +80 -2
- package/app/docs/App.tsx +72 -0
- package/app/docs/api/client.ts +303 -0
- package/app/docs/api/contract.ts +24 -0
- package/app/docs/api/docs.ts +38 -0
- package/app/docs/api/errors.ts +144 -0
- package/app/docs/boot.tsx +58 -0
- package/app/docs/components/Badge.tsx +65 -0
- package/app/docs/components/Button.tsx +37 -0
- package/app/docs/components/Card.tsx +41 -0
- package/app/docs/components/ConfirmDialog.tsx +173 -0
- package/app/docs/components/CopyButton.tsx +68 -0
- package/app/docs/components/Dialog.tsx +230 -0
- package/app/docs/components/EmptyState.tsx +34 -0
- package/app/docs/components/ErrorView.tsx +70 -0
- package/app/docs/components/Field.tsx +93 -0
- package/app/docs/components/JsonEditor.tsx +151 -0
- package/app/docs/components/JsonView.tsx +338 -0
- package/app/docs/components/KeyValue.tsx +63 -0
- package/app/docs/components/ProblemBanner.tsx +116 -0
- package/app/docs/components/ScreenErrorBoundary.tsx +141 -0
- package/app/docs/components/Spinner.tsx +33 -0
- package/app/docs/components/Table.tsx +33 -0
- package/app/docs/components/Tabs.tsx +226 -0
- package/app/docs/components/classNames.ts +4 -0
- package/app/docs/components/components.css +691 -0
- package/app/docs/components/copy.ts +89 -0
- package/app/docs/components/fieldContext.ts +29 -0
- package/app/docs/components/inputValues.ts +142 -0
- package/app/docs/components/inputs.tsx +387 -0
- package/app/docs/components/jsonParse.ts +338 -0
- package/app/docs/components/listKeys.ts +30 -0
- package/app/docs/config.ts +86 -0
- package/app/docs/context.ts +33 -0
- package/app/docs/embed.ts +74 -0
- package/app/docs/env.d.ts +2 -0
- package/app/docs/format.ts +9 -0
- package/app/docs/hooks/useUrlTab.ts +16 -0
- package/app/docs/main.tsx +11 -0
- package/app/docs/queryClient.ts +54 -0
- package/app/docs/router.tsx +194 -0
- package/app/docs/routing.ts +194 -0
- package/app/docs/screens/docs/document.ts +27 -0
- package/app/docs/screens/docs/http/OperationView.tsx +407 -0
- package/app/docs/screens/docs/http/Sidebar.tsx +190 -0
- package/app/docs/screens/docs/http/TryItPanel.tsx +603 -0
- package/app/docs/screens/docs/http/http.css +405 -0
- package/app/docs/screens/docs/http/index.tsx +395 -0
- package/app/docs/screens/docs/http/model.ts +604 -0
- package/app/docs/screens/docs/http/tryIt.ts +455 -0
- package/app/docs/screens/docs/permissions.ts +6 -0
- package/app/docs/screens/docs/schema/Prose.tsx +43 -0
- package/app/docs/screens/docs/schema/SchemaTree.tsx +319 -0
- package/app/docs/screens/docs/schema/index.ts +30 -0
- package/app/docs/screens/docs/schema/resolve.ts +466 -0
- package/app/docs/screens/docs/schema/schema.css +137 -0
- package/app/docs/screens/docs/schema/skeleton.ts +92 -0
- package/app/docs/screens/docs/viewer.tsx +154 -0
- package/app/docs/screens/docs/ws/InlineText.tsx +25 -0
- package/app/docs/screens/docs/ws/PayloadSchema.tsx +39 -0
- package/app/docs/screens/docs/ws/format.ts +82 -0
- package/app/docs/screens/docs/ws/index.tsx +286 -0
- package/app/docs/screens/docs/ws/items.tsx +786 -0
- package/app/docs/screens/docs/ws/model.ts +1178 -0
- package/app/docs/screens/docs/ws/panels.tsx +440 -0
- package/app/docs/screens/docs/ws/ws.css +231 -0
- package/app/docs/shortcuts.ts +94 -0
- package/app/docs/styles/base.css +847 -0
- package/app/docs/styles/tokens.css +143 -0
- package/app/docs/theme.ts +56 -0
- package/app/kit/unauthorized.ts +222 -0
- package/dist/docs/assets/main-0rchxtv5.js +18 -0
- package/dist/docs/assets/main-0rchxtv5.js.map +99 -0
- package/dist/docs/assets/main-cqgyvs86.css +1 -0
- package/dist/docs/manifest.json +26 -0
- package/dist/jobs/assets/AddFlowDialog-h6xkgx9b.js +6 -0
- package/dist/jobs/assets/AddFlowDialog-h6xkgx9b.js.map +11 -0
- package/dist/jobs/assets/AddFlowDialog-n7gtc84v.css +1 -0
- package/dist/jobs/assets/added-cbwsxv3e.js +4 -0
- package/dist/jobs/assets/added-cbwsxv3e.js.map +11 -0
- package/dist/jobs/assets/bodies-yyw5hmrp.js +6 -0
- package/dist/jobs/assets/bodies-yyw5hmrp.js.map +10 -0
- package/dist/jobs/assets/channel-tgkvqm8y.js +4 -0
- package/dist/jobs/assets/channel-tgkvqm8y.js.map +10 -0
- package/dist/jobs/assets/channelChecks-s4yz4yhw.js +4 -0
- package/dist/jobs/assets/channelChecks-s4yz4yhw.js.map +10 -0
- package/dist/jobs/assets/index-02427smp.css +1 -0
- package/dist/jobs/assets/index-09z2cjft.js +4 -0
- package/dist/jobs/assets/index-09z2cjft.js.map +16 -0
- package/dist/jobs/assets/index-18bn0gvy.css +1 -0
- package/dist/jobs/assets/index-47pxmyxn.js +4 -0
- package/dist/jobs/assets/index-47pxmyxn.js.map +10 -0
- package/dist/jobs/assets/index-4e55z986.js +4 -0
- package/dist/jobs/assets/index-4e55z986.js.map +11 -0
- package/dist/jobs/assets/index-5c933424.js +4 -0
- package/dist/jobs/assets/index-5c933424.js.map +18 -0
- package/dist/jobs/assets/index-5kbyqspm.css +1 -0
- package/dist/jobs/assets/index-5ng37hk6.css +1 -0
- package/dist/jobs/assets/index-5w8w7fvc.css +1 -0
- package/dist/jobs/assets/index-71qs5qyx.css +1 -0
- package/dist/jobs/assets/index-btpg4r8b.css +1 -0
- package/dist/jobs/assets/index-d5r5n0mm.js +4 -0
- package/dist/jobs/assets/index-d5r5n0mm.js.map +23 -0
- package/dist/jobs/assets/index-e0yy2skq.css +1 -0
- package/dist/jobs/assets/index-hs60b25v.js +10 -0
- package/dist/jobs/assets/index-hs60b25v.js.map +62 -0
- package/dist/jobs/assets/index-k2smrr70.js +4 -0
- package/dist/jobs/assets/index-k2smrr70.js.map +9 -0
- package/dist/jobs/assets/index-q24jw3w9.js +4 -0
- package/dist/jobs/assets/index-q24jw3w9.js.map +32 -0
- package/dist/jobs/assets/index-qfb80xen.css +1 -0
- package/dist/jobs/assets/index-rtynx6wj.js +4 -0
- package/dist/jobs/assets/index-rtynx6wj.js.map +10 -0
- package/dist/jobs/assets/live-6k7s6358.js +4 -0
- package/dist/jobs/assets/live-6k7s6358.js.map +10 -0
- package/dist/jobs/assets/main-0jmeh6mq.js +6 -0
- package/dist/jobs/assets/main-0jmeh6mq.js.map +12 -0
- package/dist/jobs/assets/main-3yj6bv90.js +5 -0
- package/dist/jobs/assets/main-3yj6bv90.js.map +10 -0
- package/dist/jobs/assets/main-4g69fn3x.js +5 -0
- package/dist/jobs/assets/main-4g69fn3x.js.map +10 -0
- package/dist/jobs/assets/main-4gte3eqq.js +5 -0
- package/dist/jobs/assets/main-4gte3eqq.js.map +10 -0
- package/dist/jobs/assets/main-4j1fcevh.js +5 -0
- package/dist/jobs/assets/main-4j1fcevh.js.map +10 -0
- package/dist/jobs/assets/main-54qwkz28.css +1 -0
- package/dist/jobs/assets/main-5grjvsy7.js +5 -0
- package/dist/jobs/assets/main-5grjvsy7.js.map +11 -0
- package/dist/jobs/assets/main-5zbx60gq.js +5 -0
- package/dist/jobs/assets/main-5zbx60gq.js.map +11 -0
- package/dist/jobs/assets/main-9prgjhg4.js +5 -0
- package/dist/jobs/assets/main-9prgjhg4.js.map +10 -0
- package/dist/jobs/assets/main-a250ffk7.js +17 -0
- package/dist/jobs/assets/main-a250ffk7.js.map +115 -0
- package/dist/jobs/assets/main-d6ge7w08.js +5 -0
- package/dist/jobs/assets/main-d6ge7w08.js.map +10 -0
- package/dist/jobs/assets/main-gngzht43.js +5 -0
- package/dist/jobs/assets/main-gngzht43.js.map +10 -0
- package/dist/jobs/assets/main-hg0hrg8j.js +5 -0
- package/dist/jobs/assets/main-hg0hrg8j.js.map +10 -0
- package/dist/jobs/assets/main-hm1ng3yg.js +5 -0
- package/dist/jobs/assets/main-hm1ng3yg.js.map +12 -0
- package/dist/jobs/assets/main-jkvd92z9.js +5 -0
- package/dist/jobs/assets/main-jkvd92z9.js.map +10 -0
- package/dist/jobs/assets/main-jv5xqd13.js +5 -0
- package/dist/jobs/assets/main-jv5xqd13.js.map +21 -0
- package/dist/jobs/assets/main-jxre7w93.js +5 -0
- package/dist/jobs/assets/main-jxre7w93.js.map +23 -0
- package/dist/jobs/assets/main-kwmqkjfx.js +5 -0
- package/dist/jobs/assets/main-kwmqkjfx.js.map +11 -0
- package/dist/jobs/assets/main-n9cf6bxm.js +5 -0
- package/dist/jobs/assets/main-n9cf6bxm.js.map +11 -0
- package/dist/jobs/assets/main-pegp0ygb.js +5 -0
- package/dist/jobs/assets/main-pegp0ygb.js.map +10 -0
- package/dist/jobs/assets/main-qr573853.js +5 -0
- package/dist/jobs/assets/main-qr573853.js.map +12 -0
- package/dist/jobs/assets/main-rq97rdr4.js +4 -0
- package/dist/jobs/assets/main-rq97rdr4.js.map +22 -0
- package/dist/jobs/assets/main-szjdmydc.js +5 -0
- package/dist/jobs/assets/main-szjdmydc.js.map +13 -0
- package/dist/jobs/assets/main-tnbhrm9n.js +7 -0
- package/dist/jobs/assets/main-tnbhrm9n.js.map +11 -0
- package/dist/jobs/assets/main-ts19j4n0.js +5 -0
- package/dist/jobs/assets/main-ts19j4n0.js.map +25 -0
- package/dist/jobs/assets/main-w93x1dzs.js +5 -0
- package/dist/jobs/assets/main-w93x1dzs.js.map +10 -0
- package/dist/jobs/assets/sections-0ksdxs8m.css +1 -0
- package/dist/jobs/assets/sections-0y7w4fb4.js +4 -0
- package/dist/jobs/assets/sections-0y7w4fb4.js.map +10 -0
- package/dist/jobs/assets/sse-mn0t0fh7.js +5 -0
- package/dist/jobs/assets/sse-mn0t0fh7.js.map +11 -0
- package/dist/jobs/assets/sseChecks-t7tcm7ra.js +4 -0
- package/dist/jobs/assets/sseChecks-t7tcm7ra.js.map +10 -0
- package/dist/jobs/assets/validation-ysjfy072.js +4 -0
- package/dist/jobs/assets/validation-ysjfy072.js.map +9 -0
- package/dist/jobs/manifest.json +511 -0
- package/dts/docs/assets.d.ts +144 -0
- package/dts/docs/assets.d.ts.map +1 -0
- package/dts/docs/browser/config.d.ts +61 -0
- package/dts/docs/browser/config.d.ts.map +1 -0
- package/dts/docs/browser/index.d.ts +12 -0
- package/dts/docs/browser/index.d.ts.map +1 -0
- package/dts/docs/docsUi.d.ts +42 -0
- package/dts/docs/docsUi.d.ts.map +1 -0
- package/dts/docs/hosted.d.ts +22 -0
- package/dts/docs/hosted.d.ts.map +1 -0
- package/dts/docs/index.d.ts +18 -0
- package/dts/docs/index.d.ts.map +1 -0
- package/dts/docs/shell.d.ts +57 -0
- package/dts/docs/shell.d.ts.map +1 -0
- package/dts/docs/types.d.ts +137 -0
- package/dts/docs/types.d.ts.map +1 -0
- package/dts/index.d.ts +21 -0
- package/dts/index.d.ts.map +1 -0
- package/dts/jobs/assets.d.ts +157 -0
- package/dts/jobs/assets.d.ts.map +1 -0
- package/dts/jobs/browser/config.d.ts +67 -0
- package/dts/jobs/browser/config.d.ts.map +1 -0
- package/dts/jobs/browser/index.d.ts +10 -0
- package/dts/jobs/browser/index.d.ts.map +1 -0
- package/dts/jobs/browser/unauthorized.d.ts +9 -0
- package/dts/jobs/browser/unauthorized.d.ts.map +1 -0
- package/dts/jobs/index.d.ts +24 -0
- package/dts/jobs/index.d.ts.map +1 -0
- package/dts/jobs/jobsUi.d.ts +63 -0
- package/dts/jobs/jobsUi.d.ts.map +1 -0
- package/dts/jobs/shell.d.ts +77 -0
- package/dts/jobs/shell.d.ts.map +1 -0
- package/dts/jobs/types.d.ts +146 -0
- package/dts/jobs/types.d.ts.map +1 -0
- package/dts/kit/browser/shared.d.ts +10 -0
- package/dts/kit/browser/shared.d.ts.map +1 -0
- package/dts/kit/browser/unauthorized.d.ts +74 -0
- package/dts/kit/browser/unauthorized.d.ts.map +1 -0
- package/dts/kit/unauthorized.d.ts +47 -0
- package/dts/kit/unauthorized.d.ts.map +1 -0
- package/guides/docs.md +170 -0
- package/guides/jobs.md +1120 -0
- package/lib/docs/assets.ts +427 -0
- package/lib/docs/browser/config.ts +66 -0
- package/lib/docs/browser/index.ts +24 -0
- package/lib/docs/docsUi.ts +623 -0
- package/lib/docs/hosted.ts +31 -0
- package/lib/docs/index.ts +37 -0
- package/lib/docs/shell.ts +156 -0
- package/lib/docs/types.ts +149 -0
- package/lib/index.ts +27 -0
- package/lib/jobs/assets.ts +448 -0
- package/lib/jobs/browser/config.ts +65 -0
- package/lib/jobs/browser/index.ts +9 -0
- package/lib/jobs/browser/unauthorized.ts +11 -0
- package/lib/jobs/index.ts +41 -0
- package/lib/jobs/jobsUi.ts +709 -0
- package/lib/jobs/shell.ts +205 -0
- package/lib/jobs/types.ts +162 -0
- package/lib/kit/browser/shared.ts +11 -0
- package/lib/kit/browser/unauthorized.ts +114 -0
- package/lib/kit/unauthorized.ts +222 -0
- package/package.json +198 -4
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The app bundle, as the server serves it: every file in memory, keyed by its
|
|
3
|
+
* hashed name, with the content type, SRI integrity and ETag worked out once.
|
|
4
|
+
*
|
|
5
|
+
* Two sources, one shape. A published package ships `dist/` (built on
|
|
6
|
+
* `prepack` by `scripts/build.ts`): `dist/manifest.json` plus the hashed files
|
|
7
|
+
* under `dist/assets/`. Inside the repo `dist/` is usually absent, so the same
|
|
8
|
+
* {@link buildAssets} runs in memory on the first request instead, once per
|
|
9
|
+
* entry, and logs one line saying so.
|
|
10
|
+
*/
|
|
11
|
+
import type { Logger } from "@kingsleyweb/bun-common";
|
|
12
|
+
import { existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
|
|
13
|
+
import { basename, join, resolve } from "node:path";
|
|
14
|
+
|
|
15
|
+
/** The package root (`packages/bun-ui`, or `node_modules/@kingsleyweb/bun-ui`). */
|
|
16
|
+
export const PACKAGE_DIR = resolve(import.meta.dir, "..", "..");
|
|
17
|
+
|
|
18
|
+
/** The React app's entry point, bundled by {@link buildAssets}. */
|
|
19
|
+
export const DEFAULT_ENTRY = join(PACKAGE_DIR, "app", "jobs", "main.tsx");
|
|
20
|
+
|
|
21
|
+
/** Where `scripts/build.ts` writes the bundle and where `jobsUi()` looks for it. */
|
|
22
|
+
export const DEFAULT_DIST_DIR = join(PACKAGE_DIR, "dist", "jobs");
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Makes the error this module throws for a bad configuration (a missing
|
|
26
|
+
* entry, a corrupt `dist/`, `dev: false` with no build). It imports no domain
|
|
27
|
+
* package: each UI passes its own domain's `ConfigError` (`jobsUi()` passes
|
|
28
|
+
* bun-jobs'), so the bundle code never makes an entry need a peer.
|
|
29
|
+
*/
|
|
30
|
+
export type AssetsFail = (
|
|
31
|
+
message: string,
|
|
32
|
+
context: Record<string, unknown>,
|
|
33
|
+
) => Error;
|
|
34
|
+
|
|
35
|
+
/** The {@link AssetsFail} used when a caller passes none: a plain `Error`. */
|
|
36
|
+
export const plainFail: AssetsFail = (message) => new Error(message);
|
|
37
|
+
|
|
38
|
+
/** The manifest's file name inside a dist directory. */
|
|
39
|
+
export const MANIFEST_FILE = "manifest.json";
|
|
40
|
+
|
|
41
|
+
/** The sub-directory of a dist directory holding the hashed files. */
|
|
42
|
+
export const ASSETS_DIR = "assets";
|
|
43
|
+
|
|
44
|
+
/** One file of the bundle, as `dist/manifest.json` records it. */
|
|
45
|
+
export interface UiManifestFile {
|
|
46
|
+
/** The `Content-Type` it is served with, e.g. `"text/javascript; charset=utf-8"`. */
|
|
47
|
+
type: string;
|
|
48
|
+
/** Its size in bytes. */
|
|
49
|
+
size: number;
|
|
50
|
+
/** Its Subresource Integrity digest, `"sha384-<base64>"`. */
|
|
51
|
+
integrity: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** `dist/manifest.json`: what the build produced, and which files the shell links. */
|
|
55
|
+
export interface UiManifest {
|
|
56
|
+
/** Shape version of the manifest. Currently `1`. */
|
|
57
|
+
version: 1;
|
|
58
|
+
/** The files the HTML shell references directly. */
|
|
59
|
+
entry: {
|
|
60
|
+
/** File name of the entry module (loaded with `<script type="module">`). */
|
|
61
|
+
js: string;
|
|
62
|
+
/** File names of every stylesheet the build emitted, in build order. */
|
|
63
|
+
css: string[];
|
|
64
|
+
};
|
|
65
|
+
/** Every file under `dist/assets/`, by file name. */
|
|
66
|
+
files: Record<string, UiManifestFile>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** One file of the bundle, held in memory. */
|
|
70
|
+
export interface UiAsset extends UiManifestFile {
|
|
71
|
+
/** The hashed file name, e.g. `"main-3kx9a2f1.js"`. It is also the URL segment. */
|
|
72
|
+
name: string;
|
|
73
|
+
/** The bytes served. */
|
|
74
|
+
body: Uint8Array<ArrayBuffer>;
|
|
75
|
+
/** A strong ETag derived from the integrity digest, quoted. */
|
|
76
|
+
etag: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** The whole bundle, ready to serve. */
|
|
80
|
+
export interface UiAssets {
|
|
81
|
+
/** The files the shell links: the entry module and the stylesheets. */
|
|
82
|
+
entry: UiManifest["entry"];
|
|
83
|
+
/** Every file, by its hashed name. */
|
|
84
|
+
files: ReadonlyMap<string, UiAsset>;
|
|
85
|
+
/** Where the bundle came from: a `dist/` directory or an in-memory build. */
|
|
86
|
+
source: "dist" | "memory";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Options for {@link buildAssets}. */
|
|
90
|
+
export interface BuildAssetsOptions {
|
|
91
|
+
/** The entry point to bundle. Defaults to {@link DEFAULT_ENTRY}, the React app. */
|
|
92
|
+
entry?: string;
|
|
93
|
+
/** Minify the output. Defaults to `true`. */
|
|
94
|
+
minify?: boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Source maps: `"linked"` (default) emits `.map` files the bundle points
|
|
97
|
+
* at, `"external"` emits them unreferenced, `"none"` emits none.
|
|
98
|
+
*/
|
|
99
|
+
sourcemap?: "linked" | "external" | "none";
|
|
100
|
+
/**
|
|
101
|
+
* Type-check the entry and everything it imports first, with Bun.build's
|
|
102
|
+
* `check` (Bun ≥ 1.4.3; app/tsconfig.json's options), and fail the build on
|
|
103
|
+
* a type error. Defaults to `false`: `scripts/build.ts` (prepack) turns it
|
|
104
|
+
* on; the in-memory build `jobsUi()` runs on a request never does, which
|
|
105
|
+
* also keeps it off a 1.4.2 runtime. Adds about 175 ms.
|
|
106
|
+
*/
|
|
107
|
+
check?: boolean;
|
|
108
|
+
/** Makes the error for a missing entry. Defaults to {@link plainFail}. */
|
|
109
|
+
fail?: AssetsFail;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Content types by extension, so both sources agree whatever Bun reports. */
|
|
113
|
+
const CONTENT_TYPES: Record<string, string> = {
|
|
114
|
+
js: "text/javascript; charset=utf-8",
|
|
115
|
+
mjs: "text/javascript; charset=utf-8",
|
|
116
|
+
css: "text/css; charset=utf-8",
|
|
117
|
+
map: "application/json; charset=utf-8",
|
|
118
|
+
json: "application/json; charset=utf-8",
|
|
119
|
+
svg: "image/svg+xml",
|
|
120
|
+
png: "image/png",
|
|
121
|
+
jpg: "image/jpeg",
|
|
122
|
+
jpeg: "image/jpeg",
|
|
123
|
+
gif: "image/gif",
|
|
124
|
+
webp: "image/webp",
|
|
125
|
+
ico: "image/x-icon",
|
|
126
|
+
woff: "font/woff",
|
|
127
|
+
woff2: "font/woff2",
|
|
128
|
+
ttf: "font/ttf",
|
|
129
|
+
otf: "font/otf",
|
|
130
|
+
txt: "text/plain; charset=utf-8",
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/** A served file name: one segment, no dot segments, nothing a URL must escape. */
|
|
134
|
+
const FILE_NAME = /^[\w.~-]+$/;
|
|
135
|
+
|
|
136
|
+
/** The `Content-Type` for a file name, falling back to what the bundler reported. */
|
|
137
|
+
export function contentTypeFor(name: string, reported?: string): string {
|
|
138
|
+
const extension = name.slice(name.lastIndexOf(".") + 1).toLowerCase();
|
|
139
|
+
return (
|
|
140
|
+
CONTENT_TYPES[extension] ??
|
|
141
|
+
reported?.replace(/;\s*/, "; ") ??
|
|
142
|
+
"application/octet-stream"
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** The SRI digest of some bytes, `"sha384-<base64>"`. */
|
|
147
|
+
export function integrityOf(bytes: Uint8Array): string {
|
|
148
|
+
return `sha384-${new Bun.CryptoHasher("sha384").update(bytes).digest("base64")}`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Wraps bytes as a served asset. */
|
|
152
|
+
function toAsset(
|
|
153
|
+
name: string,
|
|
154
|
+
body: Uint8Array<ArrayBuffer>,
|
|
155
|
+
type: string,
|
|
156
|
+
): UiAsset {
|
|
157
|
+
const integrity = integrityOf(body);
|
|
158
|
+
return {
|
|
159
|
+
name,
|
|
160
|
+
body,
|
|
161
|
+
type,
|
|
162
|
+
size: body.byteLength,
|
|
163
|
+
integrity,
|
|
164
|
+
// The digest already identifies the bytes; the prefix and padding are noise.
|
|
165
|
+
etag: `"${integrity.slice("sha384-".length, "sha384-".length + 32)}"`,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Bundles the app with `Bun.build`, in memory: browser target, content-hashed
|
|
171
|
+
* names (`[name]-[hash].[ext]`), code splitting, `NODE_ENV=production`.
|
|
172
|
+
* Throws with the bundler's messages when the build fails.
|
|
173
|
+
*/
|
|
174
|
+
export async function buildAssets(
|
|
175
|
+
options: BuildAssetsOptions = {},
|
|
176
|
+
): Promise<UiAssets> {
|
|
177
|
+
const entry = options.entry ?? DEFAULT_ENTRY;
|
|
178
|
+
if (!existsSync(entry)) {
|
|
179
|
+
throw (options.fail ?? plainFail)(
|
|
180
|
+
`bun-ui: the app entry ${entry} does not exist`,
|
|
181
|
+
{ entry },
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
const sourcemap = options.sourcemap ?? "linked";
|
|
185
|
+
const result = await Bun.build({
|
|
186
|
+
entrypoints: [entry],
|
|
187
|
+
target: "browser",
|
|
188
|
+
format: "esm",
|
|
189
|
+
minify: options.minify ?? true,
|
|
190
|
+
splitting: true,
|
|
191
|
+
sourcemap,
|
|
192
|
+
naming: {
|
|
193
|
+
entry: "[name]-[hash].[ext]",
|
|
194
|
+
chunk: "[name]-[hash].[ext]",
|
|
195
|
+
asset: "[name]-[hash].[ext]",
|
|
196
|
+
},
|
|
197
|
+
define: { "process.env.NODE_ENV": JSON.stringify("production") },
|
|
198
|
+
throw: false,
|
|
199
|
+
// Spread rather than written out: `@types/bun` 1.4.2 pins bun-types
|
|
200
|
+
// 1.4.2, whose `BuildConfig` predates `check`. Write `check` directly
|
|
201
|
+
// once `@types/bun` 1.4.3 is the floor.
|
|
202
|
+
...(options.check === true ? { check: true } : {}),
|
|
203
|
+
});
|
|
204
|
+
if (!result.success) {
|
|
205
|
+
// A type error from `check` is "TS2322: …" alone; its position says where.
|
|
206
|
+
const messages = result.logs.map((log) =>
|
|
207
|
+
log.position === null
|
|
208
|
+
? log.message
|
|
209
|
+
: `${log.position.file}:${log.position.line}:${log.position.column}: ${log.message}`,
|
|
210
|
+
);
|
|
211
|
+
throw new Error(
|
|
212
|
+
`bun-ui: building ${entry} failed:\n${messages.join("\n")}`,
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const files = new Map<string, UiAsset>();
|
|
217
|
+
let js: string | undefined;
|
|
218
|
+
const css: string[] = [];
|
|
219
|
+
for (const output of result.outputs) {
|
|
220
|
+
const name = basename(output.path);
|
|
221
|
+
const body = new Uint8Array(await output.arrayBuffer());
|
|
222
|
+
files.set(name, toAsset(name, body, contentTypeFor(name, output.type)));
|
|
223
|
+
if (output.kind === "entry-point" && name.endsWith(".js")) {
|
|
224
|
+
js = name;
|
|
225
|
+
} else if (name.endsWith(".css")) {
|
|
226
|
+
css.push(name);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (js === undefined) {
|
|
230
|
+
throw new Error(`bun-ui: building ${entry} produced no entry module`);
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
entry: { js, css: entryStylesheets(js, css) },
|
|
234
|
+
files,
|
|
235
|
+
source: "memory",
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** A hashed file name without its hash and extension: `main-3kx9.css` → `main`. */
|
|
240
|
+
function stemOf(name: string): string {
|
|
241
|
+
return name.replace(/-[a-z0-9]+\.[a-z]+$/, "");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* The stylesheets the shell links. With code splitting, Bun emits the entry's
|
|
246
|
+
* stylesheet holding every rule, lazily imported ones included, plus one per
|
|
247
|
+
* split chunk repeating that chunk's rules, and marks them all "asset". The
|
|
248
|
+
* entry's is the one named after the entry module; linking the others too
|
|
249
|
+
* would download the same rules twice. If none matches (a bundler change),
|
|
250
|
+
* every stylesheet is linked, which is redundant but never unstyled.
|
|
251
|
+
*/
|
|
252
|
+
export function entryStylesheets(js: string, css: readonly string[]): string[] {
|
|
253
|
+
const own = css.filter((name) => stemOf(name) === stemOf(js));
|
|
254
|
+
return own.length > 0 ? own : [...css];
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/** The manifest describing a bundle. */
|
|
258
|
+
export function manifestOf(assets: UiAssets): UiManifest {
|
|
259
|
+
const files: Record<string, UiManifestFile> = {};
|
|
260
|
+
for (const [name, file] of assets.files) {
|
|
261
|
+
files[name] = {
|
|
262
|
+
type: file.type,
|
|
263
|
+
size: file.size,
|
|
264
|
+
integrity: file.integrity,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
version: 1,
|
|
269
|
+
entry: { js: assets.entry.js, css: [...assets.entry.css] },
|
|
270
|
+
files,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Writes a bundle as a dist directory: the files under `<dir>/assets/` (the
|
|
276
|
+
* directory is emptied first, so stale hashes do not accumulate) and
|
|
277
|
+
* `<dir>/manifest.json`. Returns the manifest written.
|
|
278
|
+
*/
|
|
279
|
+
export async function writeAssets(
|
|
280
|
+
assets: UiAssets,
|
|
281
|
+
dir: string = DEFAULT_DIST_DIR,
|
|
282
|
+
): Promise<UiManifest> {
|
|
283
|
+
const assetsDir = join(dir, ASSETS_DIR);
|
|
284
|
+
rmSync(assetsDir, { recursive: true, force: true });
|
|
285
|
+
mkdirSync(assetsDir, { recursive: true });
|
|
286
|
+
for (const [name, file] of assets.files) {
|
|
287
|
+
await Bun.write(join(assetsDir, name), file.body);
|
|
288
|
+
}
|
|
289
|
+
const manifest = manifestOf(assets);
|
|
290
|
+
await Bun.write(
|
|
291
|
+
join(dir, MANIFEST_FILE),
|
|
292
|
+
`${JSON.stringify(manifest, null, 2)}\n`,
|
|
293
|
+
);
|
|
294
|
+
return manifest;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** Whether `dir` holds a built bundle (its manifest exists). */
|
|
298
|
+
export function hasDist(dir: string = DEFAULT_DIST_DIR): boolean {
|
|
299
|
+
return existsSync(join(dir, MANIFEST_FILE));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** A string property of an unknown value, or `undefined`. */
|
|
303
|
+
function stringField(value: unknown, key: string): string | undefined {
|
|
304
|
+
const field =
|
|
305
|
+
typeof value === "object" && value !== null
|
|
306
|
+
? (value as Record<string, unknown>)[key]
|
|
307
|
+
: undefined;
|
|
308
|
+
return typeof field === "string" ? field : undefined;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Reads a dist directory into memory, synchronously. Every file the manifest
|
|
313
|
+
* lists must exist and match its recorded integrity — a half-copied or
|
|
314
|
+
* hand-edited dist fails here, with the file named, rather than as a browser
|
|
315
|
+
* refusing a script over SRI.
|
|
316
|
+
*/
|
|
317
|
+
export function loadDistAssets(
|
|
318
|
+
dir: string = DEFAULT_DIST_DIR,
|
|
319
|
+
fail: AssetsFail = plainFail,
|
|
320
|
+
): UiAssets {
|
|
321
|
+
const manifestPath = join(dir, MANIFEST_FILE);
|
|
322
|
+
const corrupt = (why: string): Error =>
|
|
323
|
+
fail(
|
|
324
|
+
`bun-ui: ${manifestPath} is not a usable build (${why}); rebuild it with \`bun scripts/build.ts\``,
|
|
325
|
+
{ dir },
|
|
326
|
+
);
|
|
327
|
+
let manifest: unknown;
|
|
328
|
+
try {
|
|
329
|
+
manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
330
|
+
} catch (error) {
|
|
331
|
+
throw corrupt(error instanceof Error ? error.message : String(error));
|
|
332
|
+
}
|
|
333
|
+
const record = manifest as Partial<UiManifest> | null;
|
|
334
|
+
if (
|
|
335
|
+
typeof record !== "object" ||
|
|
336
|
+
record === null ||
|
|
337
|
+
record.version !== 1 ||
|
|
338
|
+
typeof record.files !== "object" ||
|
|
339
|
+
record.files === null
|
|
340
|
+
) {
|
|
341
|
+
throw corrupt("unknown manifest version or shape");
|
|
342
|
+
}
|
|
343
|
+
const js = stringField(record.entry, "js");
|
|
344
|
+
const cssList = (record.entry as { css?: unknown } | undefined)?.css;
|
|
345
|
+
if (
|
|
346
|
+
js === undefined ||
|
|
347
|
+
!Array.isArray(cssList) ||
|
|
348
|
+
!cssList.every((name) => typeof name === "string")
|
|
349
|
+
) {
|
|
350
|
+
throw corrupt("no entry");
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const files = new Map<string, UiAsset>();
|
|
354
|
+
for (const [name, info] of Object.entries(record.files)) {
|
|
355
|
+
if (!FILE_NAME.test(name) || name === "." || name === "..") {
|
|
356
|
+
throw corrupt(`bad file name ${JSON.stringify(name)}`);
|
|
357
|
+
}
|
|
358
|
+
let body: Uint8Array<ArrayBuffer>;
|
|
359
|
+
try {
|
|
360
|
+
body = new Uint8Array(readFileSync(join(dir, ASSETS_DIR, name)));
|
|
361
|
+
} catch {
|
|
362
|
+
throw corrupt(`${name} is missing`);
|
|
363
|
+
}
|
|
364
|
+
const asset = toAsset(
|
|
365
|
+
name,
|
|
366
|
+
body,
|
|
367
|
+
stringField(info, "type") ?? contentTypeFor(name),
|
|
368
|
+
);
|
|
369
|
+
if (asset.integrity !== stringField(info, "integrity")) {
|
|
370
|
+
throw corrupt(`${name} does not match its recorded integrity`);
|
|
371
|
+
}
|
|
372
|
+
files.set(name, asset);
|
|
373
|
+
}
|
|
374
|
+
for (const name of [js, ...cssList]) {
|
|
375
|
+
if (!files.has(name)) {
|
|
376
|
+
throw corrupt(`the entry file ${name} is not in the manifest`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return { entry: { js, css: cssList }, files, source: "dist" };
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/** In-memory builds, by entry path: each entry is built at most once per process. */
|
|
383
|
+
const memoryBuilds = new Map<string, Promise<UiAssets>>();
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Builds an entry in memory, once per process: concurrent and later callers
|
|
387
|
+
* share the first build. A failed build is forgotten, so the next call tries
|
|
388
|
+
* again. Logs one `info` line when a build starts.
|
|
389
|
+
*/
|
|
390
|
+
export function buildAssetsOnce(
|
|
391
|
+
entry: string,
|
|
392
|
+
logger: Logger,
|
|
393
|
+
): Promise<UiAssets> {
|
|
394
|
+
const key = resolve(entry);
|
|
395
|
+
let build = memoryBuilds.get(key);
|
|
396
|
+
if (build === undefined) {
|
|
397
|
+
logger.info(
|
|
398
|
+
"bun-ui: no dist/jobs build, bundling the app in memory (run `bun scripts/build.ts` to prebuild)",
|
|
399
|
+
{ entry: key },
|
|
400
|
+
);
|
|
401
|
+
build = buildAssets({ entry: key });
|
|
402
|
+
memoryBuilds.set(key, build);
|
|
403
|
+
build.catch(() => {
|
|
404
|
+
if (memoryBuilds.get(key) === build) {
|
|
405
|
+
memoryBuilds.delete(key);
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
return build;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/** Where `jobsUi()` gets its bundle from, and how. */
|
|
413
|
+
export interface AssetSourceOptions {
|
|
414
|
+
/** `false`: require `distDir`; `true`: always build `entry` in memory; `undefined`: dist when present, else build. */
|
|
415
|
+
dev: boolean | undefined;
|
|
416
|
+
/** The dist directory to read. */
|
|
417
|
+
distDir: string;
|
|
418
|
+
/** The entry to build in memory. */
|
|
419
|
+
entry: string;
|
|
420
|
+
/** Receives the one-line notice of an in-memory build. */
|
|
421
|
+
logger: Logger;
|
|
422
|
+
/** Makes the error for `dev: false` with no build, or a corrupt one: the UI's domain `ConfigError`. */
|
|
423
|
+
fail: AssetsFail;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Resolves the bundle source when `jobsUi()` is called. A dist directory is
|
|
428
|
+
* read synchronously right away, so a missing or corrupt one fails the call,
|
|
429
|
+
* not the first request; an in-memory build is deferred to the first request.
|
|
430
|
+
*
|
|
431
|
+
* @throws `options.fail`'s error with `dev: false` and no dist, or a corrupt dist.
|
|
432
|
+
*/
|
|
433
|
+
export function resolveAssetSource(
|
|
434
|
+
options: AssetSourceOptions,
|
|
435
|
+
): () => Promise<UiAssets> {
|
|
436
|
+
const { dev, distDir, entry, logger, fail } = options;
|
|
437
|
+
if (dev !== true && hasDist(distDir)) {
|
|
438
|
+
const assets = Promise.resolve(loadDistAssets(distDir, fail));
|
|
439
|
+
return () => assets;
|
|
440
|
+
}
|
|
441
|
+
if (dev === false) {
|
|
442
|
+
throw fail(
|
|
443
|
+
`bun-ui: no build found at ${join(distDir, MANIFEST_FILE)} and \`dev: false\` forbids building in memory; run \`bun scripts/build.ts\` in the package, or leave \`dev\` unset`,
|
|
444
|
+
{ distDir },
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
return () => buildAssetsOnce(entry, logger);
|
|
448
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The contract between `jobsUi()` (server, `lib/jobs/`) and the React app (`app/jobs/`).
|
|
3
|
+
*
|
|
4
|
+
* The server resolves this once per mount and inlines it into the HTML shell as
|
|
5
|
+
* `<script type="application/json" id="bun-jobs-ui-config" nonce="…">`. The app
|
|
6
|
+
* reads it before its first request. It must stay free of runtime imports: both
|
|
7
|
+
* sides import it, and the app side is bundled for the browser.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { UiTheme } from "../../kit/browser/shared";
|
|
11
|
+
import type { UiUnauthorized } from "./unauthorized";
|
|
12
|
+
|
|
13
|
+
export type { UiTheme };
|
|
14
|
+
|
|
15
|
+
/** Id of the `<script type="application/json">` element carrying {@link JobsUiConfig}. */
|
|
16
|
+
export const JOBS_UI_CONFIG_ELEMENT_ID = "bun-jobs-ui-config";
|
|
17
|
+
|
|
18
|
+
/** Sections of the app a mount can switch off. */
|
|
19
|
+
export interface JobsUiSections {
|
|
20
|
+
/** Job/queue/runner management screens. Defaults to `true`. */
|
|
21
|
+
manage: boolean;
|
|
22
|
+
/** The OpenAPI and AsyncAPI documentation viewers. Defaults to `true`. */
|
|
23
|
+
docs: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Resolved configuration handed to the browser. Never carries secrets. */
|
|
27
|
+
export interface JobsUiConfig {
|
|
28
|
+
/** Shape version of this object, bumped on a breaking change. Currently `1`. */
|
|
29
|
+
version: 1;
|
|
30
|
+
/** Page title and brand text. Defaults to `"Jobs"`. */
|
|
31
|
+
title: string;
|
|
32
|
+
/** Absolute path the UI is mounted at, no trailing slash (e.g. `"/jobs"`). Client routes live under it. */
|
|
33
|
+
basePath: string;
|
|
34
|
+
/** Path the hashed assets are served from, e.g. `"/jobs/assets"`. */
|
|
35
|
+
assetsPath: string;
|
|
36
|
+
/**
|
|
37
|
+
* Base URL of the management API with no trailing slash: a same-origin
|
|
38
|
+
* absolute path (`"/jobs-api"`) or, with `apiUrl`, a full origin-qualified URL.
|
|
39
|
+
*/
|
|
40
|
+
apiBase: string;
|
|
41
|
+
/**
|
|
42
|
+
* Header every mutation must carry (any non-empty value), or `null` when the
|
|
43
|
+
* API has no CSRF header configured. Mirrors the API's `csrf.header`.
|
|
44
|
+
*/
|
|
45
|
+
csrfHeader: string | null;
|
|
46
|
+
/**
|
|
47
|
+
* The API's WebSocket endpoint, when it has one: `path` is the full path
|
|
48
|
+
* (including the API basePath); `port` is set only for a dedicated-port
|
|
49
|
+
* socket. `null` when the API has no socket. `/meta` remains the authority at
|
|
50
|
+
* runtime; this lets the app connect without waiting for it.
|
|
51
|
+
*/
|
|
52
|
+
websocket: { path: string; port: number | null } | null;
|
|
53
|
+
/** Full paths of the API's spec documents, or `null` when docs are off. */
|
|
54
|
+
docs: { openapi: string; asyncapi: string | null } | null;
|
|
55
|
+
/** Which sections are enabled. */
|
|
56
|
+
sections: JobsUiSections;
|
|
57
|
+
/** Initial colour scheme. */
|
|
58
|
+
theme: UiTheme;
|
|
59
|
+
/**
|
|
60
|
+
* Where an authorization failure sends the browser (`jobsUi()`'s
|
|
61
|
+
* `unauthorized`), or `null` when it is off: the app then shows each
|
|
62
|
+
* screen's own message, as before.
|
|
63
|
+
*/
|
|
64
|
+
unauthorized: UiUnauthorized | null;
|
|
65
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@kingsleyweb/bun-ui/jobs/browser`: the jobs UI's browser-safe contract,
|
|
3
|
+
* what `jobsUi()` inlines into the page and the app reads before its first
|
|
4
|
+
* request. No runtime import beyond constants and no domain package, so a
|
|
5
|
+
* browser bundle (or a host reading the injected config) may import it.
|
|
6
|
+
*/
|
|
7
|
+
export type { JobsUiConfig, JobsUiSections, UiTheme } from "./config";
|
|
8
|
+
export { JOBS_UI_CONFIG_ELEMENT_ID } from "./config";
|
|
9
|
+
export type { UiUnauthorized, UiUnauthorizedStatus } from "./unauthorized";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `unauthorized` option's shared types, re-exported from the kit
|
|
3
|
+
* (`lib/kit/browser/unauthorized.ts`), which `docsUi()` uses too, as it does
|
|
4
|
+
* the code both run: validation and the shell's `303` (`lib/kit/unauthorized`),
|
|
5
|
+
* and the browser's redirect (`app/kit/unauthorized`). Types only, so
|
|
6
|
+
* `lib/jobs/browser` stays free of runtime imports.
|
|
7
|
+
*/
|
|
8
|
+
export type {
|
|
9
|
+
UiUnauthorized,
|
|
10
|
+
UiUnauthorizedStatus,
|
|
11
|
+
} from "../../kit/browser/unauthorized";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export {
|
|
2
|
+
ASSET_CACHE_CONTROL,
|
|
3
|
+
DEFAULT_BASE_PATH,
|
|
4
|
+
DEFAULT_TITLE,
|
|
5
|
+
jobsUi,
|
|
6
|
+
} from "./jobsUi";
|
|
7
|
+
/**
|
|
8
|
+
* `@kingsleyweb/bun-ui/jobs`: a management UI and API documentation viewer
|
|
9
|
+
* for the `@kingsleyweb/bun-jobs` management API. Needs
|
|
10
|
+
* `@kingsleyweb/bun-jobs`, an optional peer of the package that this entry
|
|
11
|
+
* (and `./lib/jobs/*`) alone requires.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* const api = createJobsApi({ jobs, basePath: "/jobs-api", authorize });
|
|
15
|
+
* const ui = jobsUi({ api, basePath: "/jobs" });
|
|
16
|
+
* app.use(api.basePath, api.router);
|
|
17
|
+
* app.use(ui.basePath, ui.router);
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* The bundle machinery (`lib/jobs/assets.ts`: building, writing and loading
|
|
21
|
+
* `dist/jobs/`) is internal: `scripts/build.ts` and the tests import it directly.
|
|
22
|
+
* `JobsUiBaseOptions` stays public because both option interfaces extend it,
|
|
23
|
+
* so it is part of the options' declared shape.
|
|
24
|
+
*/
|
|
25
|
+
export type {
|
|
26
|
+
UiUnauthorized,
|
|
27
|
+
UiUnauthorizedStatus,
|
|
28
|
+
} from "./browser/unauthorized";
|
|
29
|
+
export type { JobsUiConfig, JobsUiSections, UiTheme } from "./browser/config";
|
|
30
|
+
export { JOBS_UI_CONFIG_ELEMENT_ID } from "./browser/config";
|
|
31
|
+
export type {
|
|
32
|
+
JobsUi,
|
|
33
|
+
JobsUiApiOptions,
|
|
34
|
+
JobsUiUnauthorizedOptions,
|
|
35
|
+
JobsUiAuthorize,
|
|
36
|
+
JobsUiAuthorizeContext,
|
|
37
|
+
JobsUiAuthorizeResult,
|
|
38
|
+
JobsUiBaseOptions,
|
|
39
|
+
JobsUiOptions,
|
|
40
|
+
JobsUiUrlOptions,
|
|
41
|
+
} from "./types";
|