@kingsleyweb/bun-ui 0.0.0-stage → 3.0.0-rc.1
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-cqgyvs86.css +1 -0
- package/dist/docs/assets/main-pgxtmtja.js +18 -0
- package/dist/docs/assets/main-pgxtmtja.js.map +99 -0
- package/dist/docs/manifest.json +26 -0
- package/dist/jobs/assets/AddFlowDialog-0ec5ff4r.js +6 -0
- package/dist/jobs/assets/AddFlowDialog-0ec5ff4r.js.map +11 -0
- package/dist/jobs/assets/AddFlowDialog-n7gtc84v.css +1 -0
- package/dist/jobs/assets/added-ma7xb2dx.js +4 -0
- package/dist/jobs/assets/added-ma7xb2dx.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-a5chwqet.js +4 -0
- package/dist/jobs/assets/channel-a5chwqet.js.map +10 -0
- package/dist/jobs/assets/channelChecks-1epbhv9a.js +4 -0
- package/dist/jobs/assets/channelChecks-1epbhv9a.js.map +10 -0
- package/dist/jobs/assets/index-02427smp.css +1 -0
- package/dist/jobs/assets/index-18bn0gvy.css +1 -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-7kbae3pm.js +4 -0
- package/dist/jobs/assets/index-7kbae3pm.js.map +11 -0
- package/dist/jobs/assets/index-btpg4r8b.css +1 -0
- package/dist/jobs/assets/index-c7qk888t.js +4 -0
- package/dist/jobs/assets/index-c7qk888t.js.map +10 -0
- package/dist/jobs/assets/index-de1fgbgf.js +4 -0
- package/dist/jobs/assets/index-de1fgbgf.js.map +9 -0
- package/dist/jobs/assets/index-e0yy2skq.css +1 -0
- package/dist/jobs/assets/index-j3h4v03g.js +4 -0
- package/dist/jobs/assets/index-j3h4v03g.js.map +23 -0
- package/dist/jobs/assets/index-m7zk18cj.js +4 -0
- package/dist/jobs/assets/index-m7zk18cj.js.map +16 -0
- package/dist/jobs/assets/index-mykmd5cv.js +4 -0
- package/dist/jobs/assets/index-mykmd5cv.js.map +10 -0
- package/dist/jobs/assets/index-pjtmzrcd.js +4 -0
- package/dist/jobs/assets/index-pjtmzrcd.js.map +32 -0
- package/dist/jobs/assets/index-qfb80xen.css +1 -0
- package/dist/jobs/assets/index-xfq292gs.js +4 -0
- package/dist/jobs/assets/index-xfq292gs.js.map +18 -0
- package/dist/jobs/assets/index-y5zw06cs.js +10 -0
- package/dist/jobs/assets/index-y5zw06cs.js.map +62 -0
- package/dist/jobs/assets/live-erz9pvnm.js +4 -0
- package/dist/jobs/assets/live-erz9pvnm.js.map +10 -0
- package/dist/jobs/assets/main-2n5wnj51.js +5 -0
- package/dist/jobs/assets/main-2n5wnj51.js.map +12 -0
- package/dist/jobs/assets/main-3xxhy5jh.js +5 -0
- package/dist/jobs/assets/main-3xxhy5jh.js.map +11 -0
- package/dist/jobs/assets/main-47391fgm.js +5 -0
- package/dist/jobs/assets/main-47391fgm.js.map +23 -0
- package/dist/jobs/assets/main-54qwkz28.css +1 -0
- package/dist/jobs/assets/main-6xdkyzv1.js +5 -0
- package/dist/jobs/assets/main-6xdkyzv1.js.map +10 -0
- package/dist/jobs/assets/main-7jzem39c.js +4 -0
- package/dist/jobs/assets/main-7jzem39c.js.map +22 -0
- package/dist/jobs/assets/main-9qd3nqs5.js +5 -0
- package/dist/jobs/assets/main-9qd3nqs5.js.map +12 -0
- package/dist/jobs/assets/main-b35rfpdf.js +5 -0
- package/dist/jobs/assets/main-b35rfpdf.js.map +11 -0
- package/dist/jobs/assets/main-e8zsqez2.js +5 -0
- package/dist/jobs/assets/main-e8zsqez2.js.map +10 -0
- package/dist/jobs/assets/main-eekxtm5w.js +5 -0
- package/dist/jobs/assets/main-eekxtm5w.js.map +25 -0
- package/dist/jobs/assets/main-fcq7hfk0.js +5 -0
- package/dist/jobs/assets/main-fcq7hfk0.js.map +10 -0
- package/dist/jobs/assets/main-fn8r2zyb.js +5 -0
- package/dist/jobs/assets/main-fn8r2zyb.js.map +10 -0
- package/dist/jobs/assets/main-grcj8v1z.js +5 -0
- package/dist/jobs/assets/main-grcj8v1z.js.map +21 -0
- package/dist/jobs/assets/main-gzppw8ng.js +5 -0
- package/dist/jobs/assets/main-gzppw8ng.js.map +11 -0
- package/dist/jobs/assets/main-jex4pqaa.js +7 -0
- package/dist/jobs/assets/main-jex4pqaa.js.map +11 -0
- package/dist/jobs/assets/main-kjywj2zd.js +5 -0
- package/dist/jobs/assets/main-kjywj2zd.js.map +10 -0
- package/dist/jobs/assets/main-pqke97qp.js +5 -0
- package/dist/jobs/assets/main-pqke97qp.js.map +13 -0
- package/dist/jobs/assets/main-qahvywvw.js +5 -0
- package/dist/jobs/assets/main-qahvywvw.js.map +10 -0
- package/dist/jobs/assets/main-smabxbn4.js +6 -0
- package/dist/jobs/assets/main-smabxbn4.js.map +12 -0
- package/dist/jobs/assets/main-v2f8a4g3.js +5 -0
- package/dist/jobs/assets/main-v2f8a4g3.js.map +10 -0
- package/dist/jobs/assets/main-vargz49c.js +5 -0
- package/dist/jobs/assets/main-vargz49c.js.map +11 -0
- package/dist/jobs/assets/main-xf44bv26.js +17 -0
- package/dist/jobs/assets/main-xf44bv26.js.map +115 -0
- package/dist/jobs/assets/main-yg6scp84.js +5 -0
- package/dist/jobs/assets/main-yg6scp84.js.map +10 -0
- package/dist/jobs/assets/main-yjykmsvp.js +5 -0
- package/dist/jobs/assets/main-yjykmsvp.js.map +10 -0
- package/dist/jobs/assets/main-zk3n47nq.js +5 -0
- package/dist/jobs/assets/main-zk3n47nq.js.map +10 -0
- package/dist/jobs/assets/main-zx20z2e9.js +5 -0
- package/dist/jobs/assets/main-zx20z2e9.js.map +10 -0
- package/dist/jobs/assets/sections-0ksdxs8m.css +1 -0
- package/dist/jobs/assets/sections-b570mm22.js +4 -0
- package/dist/jobs/assets/sections-b570mm22.js.map +10 -0
- package/dist/jobs/assets/sse-42e8mhar.js +5 -0
- package/dist/jobs/assets/sse-42e8mhar.js.map +11 -0
- package/dist/jobs/assets/sseChecks-0jskzcs1.js +4 -0
- package/dist/jobs/assets/sseChecks-0jskzcs1.js.map +10 -0
- package/dist/jobs/assets/validation-199pberh.js +4 -0
- package/dist/jobs/assets/validation-199pberh.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 +194 -4
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../node_modules/@tanstack/query-core/build/modern/queriesObserver.js", "../../node_modules/@tanstack/react-query/build/modern/useQueries.js"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { replaceEqualDeep, shallowEqualObjects } from \"./utils.js\";\nimport { Subscribable } from \"./subscribable.js\";\nimport { notifyManager } from \"./notifyManager.js\";\nimport { QueryObserver } from \"./queryObserver.js\";\n//#region src/queriesObserver.ts\nfunction difference(array1, array2) {\n\tconst excludeSet = new Set(array2);\n\treturn array1.filter((x) => !excludeSet.has(x));\n}\n/**\n* A `QueriesObserver` watches an array of queries at once, exposing them as\n* a single array of `QueryObserverResult`s (or, when a `combine` option is\n* given, as a combined value derived from that array). It manages one\n* internal `QueryObserver` per query, and is the primitive that framework\n* adapters (e.g. `useQueries`) build their hooks on top of.\n*\n* @example\n* ```ts\n* const observer = new QueriesObserver(queryClient, [\n* { queryKey: ['post', 1], queryFn: fetchPost },\n* { queryKey: ['post', 2], queryFn: fetchPost },\n* ])\n*\n* const unsubscribe = observer.subscribe((result) => {\n* console.log(result)\n* })\n* ```\n*/\nvar QueriesObserver = class extends Subscribable {\n\t#client;\n\t#result;\n\t#queries;\n\t#options;\n\t#observers;\n\t#combinedResult;\n\t#lastCombine;\n\t#lastResult;\n\t#lastQueryHashes;\n\t#observerMatches = [];\n\tconstructor(client, queries, options) {\n\t\tsuper();\n\t\tthis.#client = client;\n\t\tthis.#options = options;\n\t\tthis.#queries = [];\n\t\tthis.#observers = [];\n\t\tthis.#result = [];\n\t\tthis.setQueries(queries);\n\t}\n\tonSubscribe() {\n\t\tif (this.listeners.size === 1) this.#observers.forEach((observer) => {\n\t\t\tobserver.subscribe((result) => {\n\t\t\t\tthis.#onUpdate(observer, result);\n\t\t\t});\n\t\t});\n\t}\n\tonUnsubscribe() {\n\t\tif (!this.listeners.size) this.destroy();\n\t}\n\t/**\n\t* Stops observing all queries: clears all listeners and destroys every\n\t* underlying `QueryObserver` this observer manages.\n\t*/\n\tdestroy() {\n\t\tthis.listeners = /* @__PURE__ */ new Set();\n\t\tthis.#observers.forEach((observer) => {\n\t\t\tobserver.destroy();\n\t\t});\n\t}\n\t/**\n\t* Replaces the set of queries being observed. Existing `QueryObserver`s\n\t* are reused for queries that match an already-observed query hash;\n\t* observers for queries that are no longer present are destroyed, and new\n\t* observers are created and subscribed to for newly added queries.\n\t*\n\t* @example\n\t* ```ts\n\t* observer.setQueries([\n\t* { queryKey: ['post', 1], queryFn: fetchPost },\n\t* { queryKey: ['post', 3], queryFn: fetchPost },\n\t* ])\n\t* ```\n\t*/\n\tsetQueries(queries, options) {\n\t\tthis.#queries = queries;\n\t\tthis.#options = options;\n\t\tif (process.env.NODE_ENV !== \"production\") {\n\t\t\tconst queryHashes = queries.map((query) => this.#client.defaultQueryOptions(query).queryHash);\n\t\t\tif (new Set(queryHashes).size !== queryHashes.length) console.warn(\"[QueriesObserver]: Duplicate Queries found. This might result in unexpected behavior.\");\n\t\t}\n\t\tnotifyManager.batch(() => {\n\t\t\tconst prevObservers = this.#observers;\n\t\t\tconst newObserverMatches = this.#findMatchingObservers(this.#queries);\n\t\t\tnewObserverMatches.forEach((match) => match.observer.setOptions(match.defaultedQueryOptions));\n\t\t\tconst newObservers = newObserverMatches.map((match) => match.observer);\n\t\t\tconst newResult = newObservers.map((observer) => observer.getCurrentResult());\n\t\t\tconst hasLengthChange = prevObservers.length !== newObservers.length;\n\t\t\tconst hasIndexChange = newObservers.some((observer, index) => observer !== prevObservers[index]);\n\t\t\tconst hasStructuralChange = hasLengthChange || hasIndexChange;\n\t\t\tconst hasResultChange = hasStructuralChange ? true : newResult.some((result, index) => {\n\t\t\t\tconst prev = this.#result[index];\n\t\t\t\treturn !prev || !shallowEqualObjects(result, prev);\n\t\t\t});\n\t\t\tif (!hasStructuralChange && !hasResultChange) return;\n\t\t\tif (hasStructuralChange) {\n\t\t\t\tthis.#observerMatches = newObserverMatches;\n\t\t\t\tthis.#observers = newObservers;\n\t\t\t}\n\t\t\tthis.#result = newResult;\n\t\t\tif (!this.hasListeners()) return;\n\t\t\tif (hasStructuralChange) {\n\t\t\t\tdifference(prevObservers, newObservers).forEach((observer) => {\n\t\t\t\t\tobserver.destroy();\n\t\t\t\t});\n\t\t\t\tdifference(newObservers, prevObservers).forEach((observer) => {\n\t\t\t\t\tobserver.subscribe((result) => {\n\t\t\t\t\t\tthis.#onUpdate(observer, result);\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\t\t\tthis.#notify();\n\t\t});\n\t}\n\t/**\n\t* Returns the most recently computed array of `QueryObserverResult`s, one\n\t* per observed query, in the same order as the queries passed to the\n\t* constructor or `setQueries`.\n\t*\n\t* @example\n\t* ```ts\n\t* const results = observer.getCurrentResult()\n\t* const data = results.map((result) => result.data)\n\t* ```\n\t*/\n\tgetCurrentResult() {\n\t\treturn this.#result;\n\t}\n\t/**\n\t* Returns the underlying `Query` instances currently being observed, in\n\t* the same order as the queries passed to the constructor or `setQueries`.\n\t*/\n\tgetQueries() {\n\t\treturn this.#observers.map((observer) => observer.getCurrentQuery());\n\t}\n\t/**\n\t* Returns the underlying `QueryObserver` instances this observer manages,\n\t* in the same order as the queries passed to the constructor or\n\t* `setQueries`.\n\t*/\n\tgetObservers() {\n\t\treturn this.#observers;\n\t}\n\t/**\n\t* The `QueriesObserver` counterpart of {@link QueryObserver#getOptimisticResult} — computes\n\t* the result for the given (already-defaulted) queries right now, synchronously. Called by\n\t* framework adapters (e.g. `useQueries`) ahead of subscribing, returning a tuple of the raw\n\t* per-query results, a function to compute the combined result from them, and a function to\n\t* wrap the results for property-access tracking.\n\t*/\n\tgetOptimisticResult(queries, combine) {\n\t\tconst matches = this.#findMatchingObservers(queries);\n\t\tconst result = matches.map((match) => match.observer.getOptimisticResult(match.defaultedQueryOptions));\n\t\tconst queryHashes = matches.map((match) => match.defaultedQueryOptions.queryHash);\n\t\treturn [\n\t\t\tresult,\n\t\t\t(r) => {\n\t\t\t\treturn this.#combineResult(r ?? result, combine, queryHashes);\n\t\t\t},\n\t\t\t() => {\n\t\t\t\treturn this.#trackResult(result, matches);\n\t\t\t}\n\t\t];\n\t}\n\t#trackResult(result, matches) {\n\t\tconst trackedProps = /* @__PURE__ */ new Set();\n\t\treturn matches.map((match, index) => {\n\t\t\tconst observerResult = result[index];\n\t\t\treturn !match.defaultedQueryOptions.notifyOnChangeProps ? match.observer.trackResult(observerResult, (accessedProp) => {\n\t\t\t\tif (!trackedProps.has(accessedProp)) {\n\t\t\t\t\ttrackedProps.add(accessedProp);\n\t\t\t\t\tmatches.forEach((m) => {\n\t\t\t\t\t\tm.observer.trackProp(accessedProp);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}) : observerResult;\n\t\t});\n\t}\n\t#combineResult(input, combine, queryHashes) {\n\t\tif (combine) {\n\t\t\tconst lastHashes = this.#lastQueryHashes;\n\t\t\tconst queryHashesChanged = queryHashes !== void 0 && lastHashes !== void 0 && (lastHashes.length !== queryHashes.length || queryHashes.some((hash, i) => hash !== lastHashes[i]));\n\t\t\tif (this.#result !== this.#lastResult || queryHashesChanged || combine !== this.#lastCombine) {\n\t\t\t\tthis.#lastCombine = combine;\n\t\t\t\tthis.#lastResult = this.#result;\n\t\t\t\tif (queryHashes !== void 0) this.#lastQueryHashes = queryHashes;\n\t\t\t\tthis.#combinedResult = replaceEqualDeep(this.#combinedResult, combine(input));\n\t\t\t}\n\t\t\treturn this.#combinedResult;\n\t\t}\n\t\treturn input;\n\t}\n\t#shouldSkipCombine() {\n\t\treturn !this.#options?.combine || this.#observers.some((observer, index) => {\n\t\t\treturn observer.options.suspense && this.#result[index]?.data === void 0;\n\t\t});\n\t}\n\t#findMatchingObservers(queries) {\n\t\tconst prevObserversMap = /* @__PURE__ */ new Map();\n\t\tthis.#observers.forEach((observer) => {\n\t\t\tconst key = observer.options.queryHash;\n\t\t\tif (!key) return;\n\t\t\tconst previousObservers = prevObserversMap.get(key);\n\t\t\tif (previousObservers) previousObservers.push(observer);\n\t\t\telse prevObserversMap.set(key, [observer]);\n\t\t});\n\t\tconst observers = [];\n\t\tqueries.forEach((options) => {\n\t\t\tconst defaultedOptions = this.#client.defaultQueryOptions(options);\n\t\t\tconst observer = prevObserversMap.get(defaultedOptions.queryHash)?.shift() ?? new QueryObserver(this.#client, defaultedOptions);\n\t\t\tobservers.push({\n\t\t\t\tdefaultedQueryOptions: defaultedOptions,\n\t\t\t\tobserver\n\t\t\t});\n\t\t});\n\t\treturn observers;\n\t}\n\t#onUpdate(observer, result) {\n\t\tconst index = this.#observers.indexOf(observer);\n\t\tif (index !== -1) {\n\t\t\tthis.#result = this.#result.slice();\n\t\t\tthis.#result[index] = result;\n\t\t\tthis.#notify();\n\t\t}\n\t}\n\t#notify() {\n\t\tif (this.hasListeners()) {\n\t\t\tconst shouldSkipCombine = this.#shouldSkipCombine();\n\t\t\tconst previousResult = this.#combinedResult;\n\t\t\tconst newResult = shouldSkipCombine ? previousResult : this.#combineResult(this.#trackResult(this.#result, this.#observerMatches), this.#options?.combine);\n\t\t\tif (shouldSkipCombine || previousResult !== newResult) notifyManager.batch(() => {\n\t\t\t\tthis.listeners.forEach((listener) => {\n\t\t\t\t\tlistener(this.#result);\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t}\n};\n//#endregion\nexport { QueriesObserver };\n\n//# sourceMappingURL=queriesObserver.js.map",
|
|
6
|
+
"\"use client\";\nimport { useQueryClient } from \"./QueryClientProvider.js\";\nimport { useIsRestoring } from \"./IsRestoringProvider.js\";\nimport { useQueryErrorResetBoundary } from \"./QueryErrorResetBoundary.js\";\nimport { ensurePreventErrorBoundaryRetry, getHasError, useClearResetErrorBoundary } from \"./errorBoundaryUtils.js\";\nimport { ensureSuspenseTimers, fetchOptimistic, shouldSuspend } from \"./suspense.js\";\nimport * as React from \"react\";\nimport { QueriesObserver, QueryObserver, noop, notifyManager } from \"@tanstack/query-core\";\n//#region src/useQueries.ts\n/**\n* The `useQueries` hook can be used to fetch a variable number of queries.\n*\n* The `queries` key accepts an array with query option objects mostly identical to `useQuery` — the top-level\n* `subscribed` option isn't accepted per query (see `placeholderData` below for another difference). A custom\n* `QueryClient` is supplied once, as `useQueries`' own top-level second argument, rather than per query.\n*\n* Having the same query key more than once in the array of query objects may cause some data to be shared\n* between queries. To avoid this, consider de-duplicating the queries and map the results back to the desired\n* structure.\n*\n* The `combine` option can be used to combine the results of the queries into a single value. The result will\n* be structurally shared to be as referentially stable as possible.\n*\n* @remarks The `combine` function only re-runs if it changed referentially, or if any of the query results\n* changed. An inlined `combine` function, as shown in the example below, therefore runs on every render — wrap\n* it in `useCallback`, or extract it to a stable function reference if it doesn't have any dependencies, to\n* avoid that.\n*\n* Unlike `useQuery`, `useQueries` cannot infer the `data` argument of an _inline_ `select` from its sibling\n* `queryFn`. Because `useQueries` infers the type of the whole `queries` array at once, the `select` parameter\n* of a query object written inline cannot be contextually typed from that same object's `queryFn`, so it falls\n* back to `unknown` — a [known TypeScript limitation](https://github.com/TanStack/query/issues/6556). Annotate\n* the `select` parameter explicitly, or define the query with {@link queryOptions}, which resolves its types in\n* a single object _before_ it reaches `useQueries`, to work around this — see the example below. The same\n* limitation applies to {@link useSuspenseQueries}.\n*\n* `placeholderData` is supported here too, but unlike `useQuery`, it doesn't receive information from\n* previously rendered queries, because the number of queries can differ between renders.\n* @param queryClient - Use this to provide a custom `QueryClient`. Otherwise, the one from the nearest context\n* will be used.\n* @returns The combined result. Without `combine`, this is an array with all the query results, in the same\n* order as the input. When `combine` is provided, this is the value returned by `combine` instead.\n*\n* @example\n* ```tsx\n* import { useQueries } from '@tanstack/react-query'\n*\n* function Posts({ ids }: { ids: Array<number> }) {\n* const postQueries = useQueries({\n* queries: ids.map((id) => ({\n* queryKey: ['post', id],\n* queryFn: () => fetchPost(id),\n* staleTime: Infinity,\n* })),\n* })\n*\n* return (\n* <ul>\n* {postQueries.map((query, index) => {\n* if (query.isPending) return <li key={ids[index]}>Loading...</li>\n* if (query.isError) return <li key={ids[index]}>Error: {query.error.message}</li>\n* return <li key={ids[index]}>{query.data.title}</li>\n* })}\n* </ul>\n* )\n* }\n* ```\n*\n* @example\n* Combining results into a single value:\n* ```tsx\n* import { useQueries } from '@tanstack/react-query'\n*\n* function Posts({ ids }: { ids: Array<number> }) {\n* const { data, isPending, isError } = useQueries({\n* queries: ids.map((id) => ({\n* queryKey: ['post', id],\n* queryFn: () => fetchPost(id),\n* })),\n* combine: (postQueries) => {\n* return {\n* data: postQueries.map((query) => query.data),\n* isPending: postQueries.some((query) => query.isPending),\n* isError: postQueries.some((query) => query.isError),\n* }\n* },\n* })\n*\n* if (isPending) return 'Loading...'\n* if (isError) return 'Error loading posts'\n*\n* return (\n* <ul>\n* {data.map((post) => (\n* <li key={post?.id}>{post?.title}</li>\n* ))}\n* </ul>\n* )\n* }\n* ```\n*\n* @example\n* Typing `select` via {@link queryOptions}. Note that spreading a `queryOptions` result and overriding\n* `select` inline still falls back to `unknown` — wrap the spread in `queryOptions` again so the override is\n* resolved before it reaches `useQueries`:\n* ```tsx\n* import { queryOptions, useQueries } from '@tanstack/react-query'\n*\n* const postOptions = (id: number) =>\n* queryOptions({\n* queryKey: ['post', id],\n* queryFn: () => fetchPost(id),\n* })\n*\n* function PostTitle({ id }: { id: number }) {\n* const [{ data: broken }] = useQueries({\n* queries: [\n* {\n* ...postOptions(id),\n* // ❌ `data` is `unknown` here\n* select: (data) => data.title,\n* },\n* ],\n* })\n*\n* const [{ data: fixed }] = useQueries({\n* queries: [\n* queryOptions({\n* ...postOptions(id),\n* // ✅ `data` is `Post`\n* select: (data) => data.title,\n* }),\n* ],\n* })\n*\n* return <h1>{fixed}</h1>\n* }\n* ```\n*/\nfunction useQueries({ queries, ...options }, queryClient) {\n\tconst client = useQueryClient(queryClient);\n\tconst isRestoring = useIsRestoring();\n\tconst errorResetBoundary = useQueryErrorResetBoundary();\n\tconst subscribed = options.subscribed !== false;\n\tconst defaultedQueries = React.useMemo(() => queries.map((opts) => {\n\t\tconst defaultedOptions = client.defaultQueryOptions(opts);\n\t\tdefaultedOptions._optimisticResults = isRestoring ? \"isRestoring\" : subscribed ? \"optimistic\" : void 0;\n\t\treturn defaultedOptions;\n\t}), [\n\t\tqueries,\n\t\tclient,\n\t\tisRestoring,\n\t\tsubscribed\n\t]);\n\tdefaultedQueries.forEach((queryOptions) => {\n\t\tensureSuspenseTimers(queryOptions);\n\t\tconst query = client.getQueryCache().get(queryOptions.queryHash);\n\t\tensurePreventErrorBoundaryRetry(queryOptions, errorResetBoundary, query);\n\t});\n\tuseClearResetErrorBoundary(errorResetBoundary);\n\tconst [observer] = React.useState(() => new QueriesObserver(client, defaultedQueries, options));\n\tconst [optimisticResult, getCombinedResult, trackResult] = observer.getOptimisticResult(defaultedQueries, options.combine);\n\tconst shouldSubscribe = !isRestoring && subscribed;\n\tReact.useSyncExternalStore(React.useCallback((onStoreChange) => shouldSubscribe ? observer.subscribe(notifyManager.batchCalls(onStoreChange)) : noop, [observer, shouldSubscribe]), () => observer.getCurrentResult(), () => observer.getCurrentResult());\n\tReact.useEffect(() => {\n\t\tobserver.setQueries(defaultedQueries, options);\n\t}, [\n\t\tdefaultedQueries,\n\t\toptions,\n\t\tobserver\n\t]);\n\tconst suspensePromises = optimisticResult.some((result, index) => shouldSuspend(defaultedQueries[index], result)) ? optimisticResult.flatMap((result, index) => {\n\t\tconst opts = defaultedQueries[index];\n\t\tif (opts && shouldSuspend(opts, result)) {\n\t\t\tconst queryObserver = new QueryObserver(client, opts);\n\t\t\treturn fetchOptimistic(opts, queryObserver, errorResetBoundary);\n\t\t}\n\t\treturn [];\n\t}) : [];\n\tif (suspensePromises.length > 0) throw Promise.all(suspensePromises);\n\tconst firstSingleResultWhichShouldThrow = optimisticResult.find((result, index) => {\n\t\tconst query = defaultedQueries[index];\n\t\treturn query && getHasError({\n\t\t\tresult,\n\t\t\terrorResetBoundary,\n\t\t\tthrowOnError: query.throwOnError,\n\t\t\tquery: client.getQueryCache().get(query.queryHash),\n\t\t\tsuspense: query.suspense\n\t\t});\n\t});\n\tif (firstSingleResultWhichShouldThrow) throw firstSingleResultWhichShouldThrow.error;\n\treturn getCombinedResult(trackResult());\n}\n//#endregion\nexport { useQueries };\n\n//# sourceMappingURL=useQueries.js.map"
|
|
7
|
+
],
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "7C3D583B967C5EF864756E2164756E21",
|
|
10
|
+
"names": []
|
|
11
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import{c,g,N,e,t,a,mr,Pt}from"./main-xf44bv26.js";class v extends Error{offset;constructor(r,n){super(r);this.offset=n}}function O(r,n){return n>=r.length?"end of input":`'${r[n]===`
|
|
2
|
+
`?"\\n":r[n]}'`}function y(r){let n=0,o=()=>{while(n<r.length&&(r[n]===" "||r[n]==="\t"||r[n]===`
|
|
3
|
+
`||r[n]==="\r"))n++},i=(s)=>{throw new v(`Expected ${s} but found ${O(r,n)}`,n)},l=(s)=>{for(let f=0;f<s.length;f++){if(r[n]!==s[f])i(`'${s}'`);n++}},d=()=>{n++;while(n<r.length){let s=r[n];if(s==='"'){n++;return}if(s==="\\"){let f=r[n+1];if(f==="u"){if(!/^[0-9a-f]{4}$/i.test(r.slice(n+2,n+6)))n+=2,i("four hex digits after \\u");n+=6;continue}if(f===void 0||!'"\\/bfnrt'.includes(f))n++,i("a valid escape");n+=2;continue}if(s<" ")throw new v("Control characters must be escaped in strings",n);n++}throw new v("Unterminated string",n)},J=()=>{let s=/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:e[+-]?\d+)?/i.exec(r.slice(n,n+400));if(!s)i("a number");n+=s[0].length},m=()=>{o();let s=r[n];if(s==="{"){if(n++,o(),r[n]==="}"){n++;return}for(;;){if(o(),r[n]!=='"')i("a property name in double quotes");if(d(),o(),r[n]!==":")i("':'");if(n++,m(),o(),r[n]===","){n++;continue}if(r[n]==="}"){n++;return}i("',' or '}'")}}if(s==="["){if(n++,o(),r[n]==="]"){n++;return}for(;;){if(m(),o(),r[n]===","){n++;continue}if(r[n]==="]"){n++;return}i("',' or ']'")}}if(s==='"'){d();return}if(s==="t"){l("true");return}if(s==="f"){l("false");return}if(s==="n"){l("null");return}if(s==="-"||s!==void 0&&s>="0"&&s<="9"){J();return}i("a value")};if(m(),o(),n<r.length)i("end of input")}function z(r,n){let o=1,i=0,l=Math.min(n,r.length);for(let d=0;d<l;d++)if(r.charCodeAt(d)===10)o++,i=d+1;return{line:o,column:l-i+1}}function I(r){try{return{ok:!0,value:JSON.parse(r)}}catch(n){let o=n instanceof Error?n.message:String(n),i=0;try{y(r)}catch(l){if(l instanceof v)o=l.message,i=l.offset}return{ok:!1,error:{message:o,offset:i,...z(r,i)}}}}function F(r){return new TextEncoder().encode(r).byteLength}function p(r){if(r<1024)return`${r} B`;if(r<1048576)return`${(r/1024).toFixed(1)} KiB`;return`${(r/1048576).toFixed(1)} MiB`}function Le(r,{maxBytes:n,allowEmpty:o=!1}={}){let i=F(r),l=n!==void 0&&i>n;if(o&&r.trim()==="")return{text:r,value:void 0,valid:!l,error:null,bytes:i,overLimit:l};let d=I(r);return d.ok?{text:r,value:d.value,valid:!l,error:null,bytes:i,overLimit:l}:{text:r,value:void 0,valid:!1,error:d.error,bytes:i,overLimit:l}}function ct({value:r,onChange:n,maxBytes:o,allowEmpty:i=!1,rows:l=10,"aria-label":d,id:J,readOnly:m=!1,disabled:s=!1,placeholder:f,className:P}){let h=mr(),L=c(),E=J??h?.id??L,j=`${E}-json-error`,k=`${E}-json-size`,u=g(()=>Le(r,{maxBytes:o,allowEmpty:i}),[r,o,i]),w=(b)=>n(Le(b,{maxBytes:o,allowEmpty:i})),S=(b)=>{if(u.error===null&&u.value!==void 0)w(JSON.stringify(u.value,null,b))},C=!m&&!s&&u.error===null&&u.value!==void 0;return t("div",{className:N("json-editor",!u.valid&&"json-editor-invalid",P),children:[e("textarea",{id:E,className:"input json-editor-text",rows:l,value:r,placeholder:f,readOnly:m,disabled:s,spellCheck:!1,autoCapitalize:"off",autoComplete:"off","aria-label":d,"aria-invalid":!u.valid||h?.["aria-invalid"]||void 0,"aria-required":h?.required,"aria-describedby":Pt(h?.["aria-describedby"],u.error&&j,k),onChange:(b)=>w(b.target.value)}),t("div",{className:"json-editor-bar",children:[u.error?t("p",{id:j,className:"json-editor-error",children:["Line ",u.error.line,", column ",u.error.column,":"," ",u.error.message]}):e("p",{className:"json-editor-ok muted",children:"Valid JSON"}),t("span",{id:k,className:N("json-editor-size",u.overLimit&&"is-over"),children:[o!==void 0&&e("meter",{className:"json-editor-meter",min:0,max:o,low:o*0.8,high:o,optimum:0,value:Math.min(u.bytes,o),"aria-hidden":"true"}),o===void 0?p(u.bytes):u.overLimit?`${p(u.bytes)} of ${p(o)}: over the limit by ${p(u.bytes-o)}`:`${p(u.bytes)} of ${p(o)}`]}),!m&&t("span",{className:"json-editor-actions",children:[e(a,{size:"sm",disabled:!C,onClick:()=>S(2),children:"Format"}),e(a,{size:"sm",disabled:!C,onClick:()=>S(void 0),children:"Minify"})]})]})]})}
|
|
4
|
+
export{Le,ct};
|
|
5
|
+
|
|
6
|
+
//# debugId=0845D5B6E51903D464756E2164756E21
|
|
7
|
+
//# sourceMappingURL=main-jex4pqaa.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["app/jobs/components/jsonParse.ts", "app/jobs/components/JsonEditor.tsx"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * JSON parsing that says *where* it failed. `JSON.parse` does the parsing;\n * only when it throws does a small scanner walk the text to find the\n * offending position, because engines disagree on whether (and how) their\n * messages carry one — JavaScriptCore's carry none at all.\n */\n\n/** Where and why a JSON text failed to parse. */\nexport interface JsonParseError {\n /** What is wrong, e.g. \"Expected ',' or '}'\". */\n message: string;\n /** 1-based line. */\n line: number;\n /** 1-based column (in UTF-16 code units). */\n column: number;\n /** 0-based offset into the text. */\n offset: number;\n}\n\n/** The outcome of {@link parseJsonText}. */\nexport type JsonParseResult =\n | {\n /** It parsed. */\n ok: true;\n /** The parsed value. */\n value: unknown;\n }\n | {\n /** It did not parse. */\n ok: false;\n /** Where and why. */\n error: JsonParseError;\n };\n\n/** Thrown inside the scanner with the failing offset. */\nclass ScanError extends Error {\n /** 0-based offset. */\n readonly offset: number;\n\n constructor(message: string, offset: number) {\n super(message);\n this.offset = offset;\n }\n}\n\n/** Describes the character at `index` for a message. */\nfunction describe(text: string, index: number): string {\n return index >= text.length\n ? \"end of input\"\n : `'${text[index] === \"\\n\" ? \"\\\\n\" : text[index]}'`;\n}\n\n/**\n * Walks `text` as JSON (RFC 8259) and throws a {@link ScanError} at the\n * first position that is not valid. Returns normally for valid JSON.\n */\nfunction scan(text: string): void {\n let index = 0;\n\n const skipWhitespace = () => {\n while (\n index < text.length &&\n (text[index] === \" \" ||\n text[index] === \"\\t\" ||\n text[index] === \"\\n\" ||\n text[index] === \"\\r\")\n ) {\n index++;\n }\n };\n\n const fail = (expected: string): never => {\n throw new ScanError(\n `Expected ${expected} but found ${describe(text, index)}`,\n index,\n );\n };\n\n const literal = (word: string) => {\n for (let i = 0; i < word.length; i++) {\n if (text[index] !== word[i]) {\n fail(`'${word}'`);\n }\n index++;\n }\n };\n\n const string = () => {\n index++; // opening quote\n while (index < text.length) {\n const char = text[index];\n if (char === '\"') {\n index++;\n return;\n }\n if (char === \"\\\\\") {\n const next = text[index + 1];\n if (next === \"u\") {\n if (!/^[0-9a-f]{4}$/i.test(text.slice(index + 2, index + 6))) {\n index += 2;\n fail(\"four hex digits after \\\\u\");\n }\n index += 6;\n continue;\n }\n if (next === undefined || !'\"\\\\/bfnrt'.includes(next)) {\n index++;\n fail(\"a valid escape\");\n }\n index += 2;\n continue;\n }\n if (char < \" \") {\n throw new ScanError(\n \"Control characters must be escaped in strings\",\n index,\n );\n }\n index++;\n }\n throw new ScanError(\"Unterminated string\", index);\n };\n\n const number = () => {\n const match = /^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:e[+-]?\\d+)?/i.exec(\n text.slice(index, index + 400),\n );\n if (!match) {\n fail(\"a number\");\n }\n index += match![0].length;\n };\n\n const value = (): void => {\n skipWhitespace();\n const char = text[index];\n if (char === \"{\") {\n index++;\n skipWhitespace();\n if (text[index] === \"}\") {\n index++;\n return;\n }\n for (;;) {\n skipWhitespace();\n if (text[index] !== '\"') {\n fail(\"a property name in double quotes\");\n }\n string();\n skipWhitespace();\n if (text[index] !== \":\") {\n fail(\"':'\");\n }\n index++;\n value();\n skipWhitespace();\n if (text[index] === \",\") {\n index++;\n continue;\n }\n if (text[index] === \"}\") {\n index++;\n return;\n }\n fail(\"',' or '}'\");\n }\n }\n if (char === \"[\") {\n index++;\n skipWhitespace();\n if (text[index] === \"]\") {\n index++;\n return;\n }\n for (;;) {\n value();\n skipWhitespace();\n if (text[index] === \",\") {\n index++;\n continue;\n }\n if (text[index] === \"]\") {\n index++;\n return;\n }\n fail(\"',' or ']'\");\n }\n }\n if (char === '\"') {\n string();\n return;\n }\n if (char === \"t\") {\n literal(\"true\");\n return;\n }\n if (char === \"f\") {\n literal(\"false\");\n return;\n }\n if (char === \"n\") {\n literal(\"null\");\n return;\n }\n if (char === \"-\" || (char !== undefined && char >= \"0\" && char <= \"9\")) {\n number();\n return;\n }\n fail(\"a value\");\n };\n\n value();\n skipWhitespace();\n if (index < text.length) {\n fail(\"end of input\");\n }\n}\n\n/** 1-based line and column of an offset. */\nexport function lineColumn(\n text: string,\n offset: number,\n): { line: number; column: number } {\n let line = 1;\n let lineStart = 0;\n const end = Math.min(offset, text.length);\n for (let i = 0; i < end; i++) {\n if (text.charCodeAt(i) === 10) {\n line++;\n lineStart = i + 1;\n }\n }\n return { line, column: end - lineStart + 1 };\n}\n\n/** Parses JSON; on failure, reports the message with its line and column. */\nexport function parseJsonText(text: string): JsonParseResult {\n try {\n return { ok: true, value: JSON.parse(text) as unknown };\n } catch (caught) {\n let message = caught instanceof Error ? caught.message : String(caught);\n let offset = 0;\n try {\n scan(text);\n // The scanner accepted what JSON.parse refused (should not happen);\n // keep the engine's message without a position.\n } catch (scanned) {\n if (scanned instanceof ScanError) {\n message = scanned.message;\n offset = scanned.offset;\n }\n }\n return {\n ok: false,\n error: { message, offset, ...lineColumn(text, offset) },\n };\n }\n}\n\n/** The UTF-8 size of a text, in bytes (what the API measures against its limits). */\nexport function byteLength(text: string): number {\n return new TextEncoder().encode(text).byteLength;\n}\n\n/** Formats a byte count: `512 B`, `1.5 KiB`, `2.0 MiB`. */\nexport function formatBytes(bytes: number): string {\n if (bytes < 1024) {\n return `${bytes} B`;\n }\n if (bytes < 1024 * 1024) {\n return `${(bytes / 1024).toFixed(1)} KiB`;\n }\n return `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;\n}\n\n/** The editor's state, as `JsonEditor` reports it on every change. */\nexport interface JsonEditorState {\n /** The raw text. */\n text: string;\n /** The parsed value; `undefined` when the text does not parse (or is empty). */\n value: unknown;\n /** Whether the text parses (or is empty and `allowEmpty`) and fits `maxBytes`. The one flag a submit button needs. */\n valid: boolean;\n /** The parse error with its line and column, or `null`. */\n error: JsonParseError | null;\n /** The text's UTF-8 size. */\n bytes: number;\n /** Whether `bytes` exceeds `maxBytes`. */\n overLimit: boolean;\n}\n\n/** Options of {@link jsonEditorState}. */\nexport interface JsonEditorStateOptions {\n /** The size limit, bytes. */\n maxBytes?: number;\n /** Whether empty (whitespace-only) text is valid, as `undefined`. Defaults to `false`. */\n allowEmpty?: boolean;\n}\n\n/**\n * Works out a {@link JsonEditorState} from text, e.g. to seed a form's\n * validity before the first edit.\n */\nexport function jsonEditorState(\n text: string,\n { maxBytes, allowEmpty = false }: JsonEditorStateOptions = {},\n): JsonEditorState {\n const bytes = byteLength(text);\n const overLimit = maxBytes !== undefined && bytes > maxBytes;\n if (allowEmpty && text.trim() === \"\") {\n return {\n text,\n value: undefined,\n valid: !overLimit,\n error: null,\n bytes,\n overLimit,\n };\n }\n const parsed = parseJsonText(text);\n return parsed.ok\n ? {\n text,\n value: parsed.value,\n valid: !overLimit,\n error: null,\n bytes,\n overLimit,\n }\n : {\n text,\n value: undefined,\n valid: false,\n error: parsed.error,\n bytes,\n overLimit,\n };\n}\n",
|
|
6
|
+
"import type { JsonEditorState, JsonEditorStateOptions } from \"./jsonParse\";\nimport { useId, useMemo } from \"react\";\nimport { Button } from \"./Button\";\nimport { cx } from \"./classNames\";\nimport { joinIds, useField } from \"./fieldContext\";\nimport { formatBytes, jsonEditorState } from \"./jsonParse\";\n\n/** Props of {@link JsonEditor}. */\nexport interface JsonEditorProps extends JsonEditorStateOptions {\n /** The text (controlled). Seed it with `JSON.stringify(value, null, 2)`. */\n value: string;\n /** Called on every edit, format and minify with the new state. */\n onChange: (state: JsonEditorState) => void;\n /** Visible rows. Defaults to `10`. */\n rows?: number;\n /** Accessible name when not inside a `Field`. */\n \"aria-label\"?: string;\n /** The textarea's id. Defaults to the enclosing field's, or a generated one. */\n id?: string;\n /** Read-only: no editing, no format/minify. */\n readOnly?: boolean;\n /** Disabled. */\n disabled?: boolean;\n /** Placeholder text. */\n placeholder?: string;\n /** Extra class names on the wrapper. */\n className?: string;\n}\n\n/**\n * A plain-textarea JSON editor: the parse error with line and column, a\n * UTF-8 size meter against `maxBytes` (turning red over it), and Format /\n * Minify. Inside a `Field` it takes the field's id and description.\n */\nexport function JsonEditor({\n value: text,\n onChange,\n maxBytes,\n allowEmpty = false,\n rows = 10,\n \"aria-label\": ariaLabel,\n id,\n readOnly = false,\n disabled = false,\n placeholder,\n className,\n}: JsonEditorProps) {\n const field = useField();\n const generatedId = useId();\n const inputId = id ?? field?.id ?? generatedId;\n const errorId = `${inputId}-json-error`;\n const sizeId = `${inputId}-json-size`;\n const state = useMemo(\n () => jsonEditorState(text, { maxBytes, allowEmpty }),\n [text, maxBytes, allowEmpty],\n );\n const emit = (next: string) =>\n onChange(jsonEditorState(next, { maxBytes, allowEmpty }));\n const reformat = (indent: number | undefined) => {\n if (state.error === null && state.value !== undefined) {\n emit(JSON.stringify(state.value, null, indent));\n }\n };\n const canFormat =\n !readOnly && !disabled && state.error === null && state.value !== undefined;\n\n return (\n <div\n className={cx(\n \"json-editor\",\n !state.valid && \"json-editor-invalid\",\n className,\n )}\n >\n <textarea\n id={inputId}\n className=\"input json-editor-text\"\n rows={rows}\n value={text}\n placeholder={placeholder}\n readOnly={readOnly}\n disabled={disabled}\n spellCheck={false}\n autoCapitalize=\"off\"\n autoComplete=\"off\"\n aria-label={ariaLabel}\n aria-invalid={!state.valid || field?.[\"aria-invalid\"] || undefined}\n aria-required={field?.required}\n aria-describedby={joinIds(\n field?.[\"aria-describedby\"],\n state.error && errorId,\n sizeId,\n )}\n onChange={(event) => emit(event.target.value)}\n />\n <div className=\"json-editor-bar\">\n {state.error ? (\n <p\n id={errorId}\n className=\"json-editor-error\"\n >\n Line {state.error.line}, column {state.error.column}:{\" \"}\n {state.error.message}\n </p>\n ) : (\n <p className=\"json-editor-ok muted\">Valid JSON</p>\n )}\n <span\n id={sizeId}\n className={cx(\"json-editor-size\", state.overLimit && \"is-over\")}\n >\n {maxBytes !== undefined && (\n <meter\n className=\"json-editor-meter\"\n min={0}\n max={maxBytes}\n low={maxBytes * 0.8}\n high={maxBytes}\n optimum={0}\n value={Math.min(state.bytes, maxBytes)}\n aria-hidden=\"true\"\n />\n )}\n {maxBytes === undefined\n ? formatBytes(state.bytes)\n : state.overLimit\n ? `${formatBytes(state.bytes)} of ${formatBytes(maxBytes)}: over the limit by ${formatBytes(state.bytes - maxBytes)}`\n : `${formatBytes(state.bytes)} of ${formatBytes(maxBytes)}`}\n </span>\n {!readOnly && (\n <span className=\"json-editor-actions\">\n <Button\n size=\"sm\"\n disabled={!canFormat}\n onClick={() => reformat(2)}\n >\n Format\n </Button>\n <Button\n size=\"sm\"\n disabled={!canFormat}\n onClick={() => reformat(undefined)}\n >\n Minify\n </Button>\n </span>\n )}\n </div>\n </div>\n );\n}\n"
|
|
7
|
+
],
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "0845D5B6E51903D464756E2164756E21",
|
|
10
|
+
"names": []
|
|
11
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{C,fe,Ke}from"./main-xf44bv26.js";var he={all:["runners"],list:()=>["runners","list"],runner:(n)=>["runner",n],detail:(n)=>["runner",n,"detail"],historyAll:(n)=>["runner",n,"history"],history:(n,t)=>["runner",n,"history",{...t}],stats:(n)=>["runner",n,"stats"]};function Ne(n){return[he.runner(n),he.all]}function mt(n){return`/runners/${C(n)}`}function s(n,t=""){return`/runners/${C(n)}${t}`}function cn(n,t){return n.request("GET","/runners",{signal:t})}async function ho(n,t,r){let e=s(t),o=await n.request("GET",e,{signal:r});return fe(o,(i)=>Ke(i,"id","name"),"a runner",e)}function go(n,t,r,e){return n.request("GET",s(t,"/history"),{query:{offset:r.offset,limit:r.limit,order:r.order},signal:e})}function yo(n,t,r){return n.request("GET",s(t,"/stats"),{signal:r})}
|
|
2
|
+
export{he,Ne,mt,cn,ho,go,yo};
|
|
3
|
+
|
|
4
|
+
//# debugId=D1DBF0D7B71339ED64756E2164756E21
|
|
5
|
+
//# sourceMappingURL=main-kjywj2zd.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["app/jobs/api/runners.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { QueryKey } from \"@tanstack/react-query\";\nimport type { ApiClient } from \"./client\";\nimport type {\n RunnerHistoryDto,\n RunnerInfoDto,\n RunnerListDto,\n RunnerStatsDto,\n} from \"./types\";\nimport { segment } from \"./client\";\nimport { assertShape, hasStrings } from \"./shape\";\n\n/**\n * The runner screens' reads and query keys.\n *\n * Everything about one runner sits under {@link runnerKeys.runner}\n * (`[\"runner\", id]`), so one invalidation refreshes the whole runner screen;\n * the runner list sits under {@link runnerKeys.all} (`[\"runners\"]`). The\n * runner actions import these keys to invalidate after a write, so keep them\n * stable.\n */\nexport const runnerKeys = {\n /** Every `GET /runners` read. */\n all: [\"runners\"] as const,\n /** `GET /runners`. */\n list: () => [\"runners\", \"list\"] as const,\n /** Everything about one runner. */\n runner: (id: string) => [\"runner\", id] as const,\n /** `GET /runners/:runner`. */\n detail: (id: string) => [\"runner\", id, \"detail\"] as const,\n /** Every `GET /runners/:runner/history`, whatever window it asked for. */\n historyAll: (id: string) => [\"runner\", id, \"history\"] as const,\n /**\n * One `GET /runners/:runner/history` window.\n *\n * The **offset is part of the key**: the route pages on the server, so two\n * windows of one runner are two different answers, and a key holding only\n * the limit would serve page 2 the cached page 1. Every invalidation goes\n * through {@link runnerKeys.historyAll} (or {@link runnerKeys.runner}\n * above it), which is the prefix of all of them, so no window is missed.\n */\n history: (id: string, window: HistoryWindow) =>\n [\"runner\", id, \"history\", { ...window }] as const,\n /** `GET /runners/:runner/stats`. */\n stats: (id: string) => [\"runner\", id, \"stats\"] as const,\n};\n\n/** What a write to one runner invalidates: the runner, and every runner list (a status shows there). */\nexport function runnerInvalidations(id: string): QueryKey[] {\n return [runnerKeys.runner(id), runnerKeys.all];\n}\n\n/** The app path of one runner (the id percent-encoded as `segment()` does). */\nexport function runnerPath(id: string): string {\n return `/runners/${segment(id)}`;\n}\n\n/** The API path of a runner, plus an optional suffix. */\nfunction base(id: string, suffix = \"\"): string {\n return `/runners/${segment(id)}${suffix}`;\n}\n\n/**\n * `GET /runners`: local runners (with name and status), then the ids of those\n * registered in other processes.\n */\nexport function listRunners(\n api: ApiClient,\n signal?: AbortSignal,\n): Promise<RunnerListDto> {\n return api.request<RunnerListDto>(\"GET\", \"/runners\", { signal });\n}\n\n/**\n * `GET /runners/:runner`. A body without a string `id` and `name` rejects\n * with an `UNEXPECTED_RESPONSE` `ApiError`, so the screen shows its error\n * state rather than an empty runner.\n */\nexport async function getRunner(\n api: ApiClient,\n id: string,\n signal?: AbortSignal,\n): Promise<RunnerInfoDto> {\n const path = base(id);\n const body = await api.request<unknown>(\"GET\", path, { signal });\n return assertShape<RunnerInfoDto>(\n body,\n (fields) => hasStrings(fields, \"id\", \"name\"),\n \"a runner\",\n path,\n );\n}\n\n/**\n * One window of `GET /runners/:runner/history`.\n *\n * `limit` bounds a **page**, not how far back `offset` may read: the route\n * caps `limit` at `limits.maxHistory` (more is 400 `VALIDATION`) and leaves\n * `offset` uncapped, so every stored run is reachable.\n */\nexport interface HistoryWindow {\n /** Runs skipped before the page. `0` is the first page. */\n offset: number;\n /** Runs on the page, `1` to `limits.maxHistory`. */\n limit: number;\n /** Which end to read from, by start time. `\"desc\"` is newest first. */\n order: \"asc\" | \"desc\";\n}\n\n/** `GET /runners/:runner/history`: one window of the runner's stored runs. */\nexport function getRunnerHistory(\n api: ApiClient,\n id: string,\n window: HistoryWindow,\n signal?: AbortSignal,\n): Promise<RunnerHistoryDto> {\n return api.request<RunnerHistoryDto>(\"GET\", base(id, \"/history\"), {\n query: { offset: window.offset, limit: window.limit, order: window.order },\n signal,\n });\n}\n\n/** `GET /runners/:runner/stats`. */\nexport function getRunnerStats(\n api: ApiClient,\n id: string,\n signal?: AbortSignal,\n): Promise<RunnerStatsDto> {\n return api.request<RunnerStatsDto>(\"GET\", base(id, \"/stats\"), { signal });\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "wCAoBO,IAAM,GAAa,CAExB,IAAK,CAAC,SAAS,EAEf,KAAM,IAAM,CAAC,UAAW,MAAM,EAE9B,OAAQ,CAAC,IAAe,CAAC,SAAU,CAAE,EAErC,OAAQ,CAAC,IAAe,CAAC,SAAU,EAAI,QAAQ,EAE/C,WAAY,CAAC,IAAe,CAAC,SAAU,EAAI,SAAS,EAUpD,QAAS,CAAC,EAAY,IACpB,CAAC,SAAU,EAAI,UAAW,IAAK,CAAO,CAAC,EAEzC,MAAO,CAAC,IAAe,CAAC,SAAU,EAAI,OAAO,CAC/C,EAGO,SAAS,EAAmB,CAAC,EAAwB,CAC1D,MAAO,CAAC,GAAW,OAAO,CAAE,EAAG,GAAW,GAAG,EAIxC,SAAS,EAAU,CAAC,EAAoB,CAC7C,MAAO,YAAY,EAAQ,CAAE,IAI/B,SAAS,CAAI,CAAC,EAAY,EAAS,GAAY,CAC7C,MAAO,YAAY,EAAQ,CAAE,IAAI,IAO5B,SAAS,EAAW,CACzB,EACA,EACwB,CACxB,OAAO,EAAI,QAAuB,MAAO,WAAY,CAAE,QAAO,CAAC,EAQ1D,eAAe,EAAS,CAC7B,EACA,EACA,EACwB,CACxB,IAAM,EAAO,EAAK,CAAE,EACd,EAAO,MAAM,EAAI,QAAiB,MAAO,EAAM,CAAE,QAAO,CAAC,EAC/D,OAAO,GACL,EACA,CAAC,IAAW,GAAW,EAAQ,KAAM,MAAM,EAC3C,WACA,CACF,EAoBK,SAAS,EAAgB,CAC9B,EACA,EACA,EACA,EAC2B,CAC3B,OAAO,EAAI,QAA0B,MAAO,EAAK,EAAI,UAAU,EAAG,CAChE,MAAO,CAAE,OAAQ,EAAO,OAAQ,MAAO,EAAO,MAAO,MAAO,EAAO,KAAM,EACzE,QACF,CAAC,EAII,SAAS,EAAc,CAC5B,EACA,EACA,EACyB,CACzB,OAAO,EAAI,QAAwB,MAAO,EAAK,EAAI,QAAQ,EAAG,CAAE,QAAO,CAAC",
|
|
8
|
+
"debugId": "D1DBF0D7B71339ED64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{r,Z,xt,K,E,C,n,e,t,s,y,i,_,fe,Ke,ia,Y,u,M,V,S,xe}from"./main-xf44bv26.js";import{Le,ct}from"./main-jex4pqaa.js";var yr=["data","returnValue","opts"],P=new Set(["completed","failed","dead"]);function kn(o){return P.has(o)}var ae={job:(o,a)=>[..._.queue(o),"job",a],stacktrace:(o,a)=>[...ae.job(o,a),"stacktrace"],logsAll:(o,a)=>[...ae.job(o,a),"logs"],logs:(o,a,d)=>[...ae.logsAll(o,a),{offset:d.offset,limit:d.limit,order:d.order}],children:(o,a)=>[...ae.job(o,a),"children"],definitions:()=>["definitions"]};function ut(o){return[_.queue(o),_.queuesAll,["overview"]]}function m(o,a){return`/queues/${C(o)}/jobs/${C(a)}`}function We(o,a){return`/queues/${encodeURIComponent(o)}/jobs/${encodeURIComponent(a)}`}function Mt(o){return`/queues/${encodeURIComponent(o)}`}function R(o){return Ke(o,"id","name","queue")&&ia(o.state)}async function br(o,a,d,l=yr,p){let f=m(a,d),g=await o.request("GET",f,{query:{include:l},signal:p});return fe(g,R,"a job",f)}function Xr(o,a,d,l,p){return o.request("GET",`${m(a,d)}/logs`,{query:{offset:l.offset,limit:l.limit,order:l.order},signal:p})}function eo(o,a,d){return o.request("DELETE",`${m(a,d)}/logs`)}function to(o,a,d,l){return o.request("GET",`${m(a,d)}/children`,{signal:l})}function no(o,a,d,l){return o.request("PATCH",m(a,d),{body:l})}async function ro(o,a,d){await o.request("DELETE",m(a,d))}function oo(o,a,d,l){return o.request("POST",`${m(a,d)}/retry`,{body:{resetAttempts:l}})}function ao(o,a,d){return o.request("POST",`${m(a,d)}/promote`)}var Un=4096;function so(o,a,d,l){return o.request("POST",`${m(a,d)}/fail`,{body:{reason:l}})}function io(o,a,d){return o.request("POST",`/queues/${C(a)}/jobs`,{body:d})}function lo(o,a,d){return o.request("POST",`/queues/${C(a)}/flows`,{body:d})}function uo(o){return[...[...new Set(o)].map((a)=>_.queue(a)),_.queuesAll,_.overviewAll]}function x(o,a){return o.request("GET","/definitions",{signal:a})}function on({addableNames:o,maxBytes:a}){return{name:o?.length===1?o[0]:"",data:Le("{}",{maxBytes:a}),jobId:"",priority:void 0,timing:"now",delay:void 0,runAt:void 0,attempts:void 0,backoff:void 0,timeout:void 0}}function A(o,a){if(o===void 0)return;if(!Number.isInteger(o))return"Enter a whole number.";return o<a?`Enter ${a} or more.`:void 0}function Er(o){if(o==="")return;if(o.length>xt)return`At most ${xt} characters (this one has ${o.length}).`;if(o.startsWith("."))return"An id cannot start with a dot.";if(/[\u0000-\u001F\u007F]/.test(o))return"An id cannot contain control characters.";return}function Ft(o){let a={};if(o.name.trim()==="")a.name="Pick or enter a job name.";else if(o.name.length>200)a.name="At most 200 characters.";if(o.data.error)a.data="The data is not valid JSON.";else if(o.data.overLimit)a.data="The data is larger than the API accepts.";else if(o.data.value===void 0)a.data="Enter the data as JSON (null is allowed).";let d=Er(o.jobId);if(d)a["opts.jobId"]=d;if(o.priority!==void 0&&!Number.isFinite(o.priority))a["opts.priority"]="Enter a number.";if(o.timing==="delay")a["opts.delay"]=o.delay===void 0?"Enter the delay in milliseconds.":A(o.delay,0);if(o.timing==="runAt"&&o.runAt===void 0)a["opts.runAt"]="Pick when it should run.";a["opts.attempts"]=A(o.attempts,1),a["opts.backoff"]=A(o.backoff,0),a["opts.timeout"]=A(o.timeout,0);for(let l of Object.keys(a))if(a[l]===void 0)delete a[l];return a}function an(o){let a={...o.jobId===""?{}:{jobId:o.jobId},...o.priority===void 0?{}:{priority:o.priority},...o.timing==="delay"&&o.delay!==void 0?{delay:o.delay}:{},...o.timing==="runAt"&&o.runAt!==void 0?{runAt:o.runAt}:{},...o.attempts===void 0?{}:{attempts:o.attempts},...o.backoff===void 0?{}:{backoff:o.backoff},...o.timeout===void 0?{}:{timeout:o.timeout}};return{name:o.name.trim(),data:o.data.value,...Object.keys(a).length>0?{opts:a}:{}}}var v={retry:"retried",promote:"promoted",remove:"removed",update:"updated",add:"added",fail:"failed",clearLogs:"cleared of its logs"},I={retry:"Only a completed, retrying, dead or delayed job can be retried.",promote:"Only a delayed job can be promoted.",remove:"Refresh to see where it is now.",update:"A new run time applies only to a waiting or delayed job, and “only in” limits the change to the states you picked.",add:"",fail:"Only a job that has not completed or died can be failed.",clearLogs:"Refresh to see where it is now."};function k(o){let a=o.context.state;return typeof a==="string"&&Z.includes(a)?a:null}function Ot(o,a){switch(o.code){case"JOB_ACTIVE":return a==="clearLogs"?"It's running: a worker picked this job up and is still writing its log, so the log cannot be cleared now. Clear it once the job finishes.":"It's running: a worker is processing this job right now, so it cannot be removed. Wait for it to finish, then try again.";case"JOB_STATE_CONFLICT":{let d=k(o);if(a==="fail"&&d==="active")return"A worker claimed the job again while it was being failed, so nothing changed. Refresh and try again.";return`${d?`The job is ${Y[d].toLowerCase()} now`:"The job is in another state now"}, so it cannot be ${v[a]}. ${I[a]}`.trim()}case"JOB_NOT_FOUND":return"The job no longer exists: it finished and was cleaned up, or somebody removed it.";case"NAME_NOT_ADDABLE":{let d=o.context.name;return`Jobs named “${typeof d==="string"?d:"this"}” may not be added over this API. Pick one of the names it allows.`}case"SERIALIZATION":return"The data cannot be stored: it is not serialisable for this queue's backend.";default:return null}}function Nr(o,a){if(!E(o))return o;let d=Ot(o,a);if(d===null)return o;return new K({kind:o.kind,status:o.status,code:o.code,title:o.title,detail:d,type:o.type,issues:o.issues,context:o.context,instance:o.instance,cause:o})}async function Je(o,a){try{return await a()}catch(d){throw Nr(d,o)}}var O=[{value:"now",label:"Now"},{value:"delay",label:"After a delay"},{value:"runAt",label:"At a time"}],w="\x00pick",J="\x00other";function sn({value:o,onChange:a,error:d,addableNames:l}){let p=s(),f=y("definitions.list"),g=l===null,D=i({queryKey:ae.definitions(),queryFn:({signal:b})=>x(p,b),enabled:g&&f,staleTime:60000}),[T,F]=r(!1);if(!g)return e(u,{label:"Name",required:!0,error:d,children:e(S,{options:[{value:"",label:"Pick a name…",disabled:!0},...l.map((b)=>({value:b}))],value:o,onChange:a})});let c=D.data?.items.map((b)=>b.name)??[],h=T||c.length===0;return t(n,{children:[c.length>0&&e(u,{label:"Name",required:!0,hint:"Any name is accepted; the defined ones are listed.",error:h?void 0:d,children:e(S,{options:[{value:w,label:"Pick a name…",disabled:!0},...c.map((b)=>({value:b})),{value:J,label:"Another name…"}],value:h?J:c.includes(o)?o:w,onChange:(b)=>{if(b===J)F(!0),a("");else F(!1),a(b)}})}),h&&e(u,{label:c.length>0?"Another name":"Name",required:!0,hint:c.length>0?void 0:"Any name is accepted. A worker must handle it for the job to run.",error:d,children:e(M,{value:o,onChange:a,maxLength:200,autoComplete:"off",spellCheck:!1})})]})}function ln({value:o,onChange:a,error:d,maxBytes:l,rows:p=8}){return e(u,{label:"Data",required:!0,hint:"JSON; null is allowed.",error:d,children:e(ct,{value:o,onChange:a,maxBytes:l,rows:p})})}function un({form:o,patch:a,fieldError:d,onRunAtProblem:l}){return t("fieldset",{className:"job-form-options",children:[e("legend",{children:"Options"}),e(u,{label:"Job id",hint:`Optional, at most ${xt} characters. An id that already exists returns that job instead.`,error:d("opts.jobId"),children:e(M,{value:o.jobId,onChange:(p)=>a({jobId:p}),maxLength:xt,autoComplete:"off",spellCheck:!1})}),e(u,{label:"Priority",hint:"Lower runs first.",error:d("opts.priority"),children:e(V,{value:o.priority,onChange:(p)=>a({priority:p}),step:"any"})}),e(u,{label:"Run",children:e(S,{options:O,value:o.timing,onChange:(p)=>a({timing:p})})}),o.timing==="delay"&&e(u,{label:"Delay (ms)",required:!0,error:d("opts.delay"),children:e(V,{value:o.delay,onChange:(p)=>a({delay:p}),min:0})}),o.timing==="runAt"&&e(u,{label:"Run at",required:!0,error:d("opts.runAt"),children:e(xe,{value:o.runAt,onChange:(p)=>a({runAt:p}),onProblem:l})}),e(u,{label:"Attempts",hint:"Attempts allowed in total, 1 or more.",error:d("opts.attempts"),children:e(V,{value:o.attempts,onChange:(p)=>a({attempts:p}),min:1})}),e(u,{label:"Backoff (ms)",hint:"Fixed delay between attempts.",error:d("opts.backoff"),children:e(V,{value:o.backoff,onChange:(p)=>a({backoff:p}),min:0})}),e(u,{label:"Timeout (ms)",hint:"How long one attempt may run.",error:d("opts.timeout"),children:e(V,{value:o.timeout,onChange:(p)=>a({timeout:p}),min:0})})]})}
|
|
2
|
+
export{yr,kn,ae,ut,We,Mt,br,Xr,eo,to,no,ro,oo,ao,Un,so,io,lo,uo,on,Er,Ft,an,Ot,Nr,Je,sn,ln,un};
|
|
3
|
+
|
|
4
|
+
//# debugId=BDEFEAB68BC650DF64756E2164756E21
|
|
5
|
+
//# sourceMappingURL=main-pqke97qp.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["app/jobs/api/jobs.ts", "app/jobs/screens/job/addJobForm.ts", "app/jobs/screens/job/jobErrors.ts", "app/jobs/screens/job/JobFormFields.tsx"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { QueryKey } from \"@tanstack/react-query\";\nimport type { ApiClient } from \"./client\";\nimport type {\n AddFlowBody,\n AddFlowResultDto,\n AddJobBody,\n AddJobResultDto,\n ChildrenDto,\n ClearJobLogsResultDto,\n DefinitionListDto,\n FailJobResultDto,\n JobDto,\n JobInclude,\n JobState,\n LogPageDto,\n PromoteJobResultDto,\n RetryJobResultDto,\n SortOrder,\n UpdateJobBody,\n} from \"./types\";\nimport { segment } from \"./client\";\nimport { queryKeys } from \"./queryKeys\";\nimport { assertShape, hasStrings, isJobState } from \"./shape\";\n\n/**\n * One job's reads and writes (`/queues/:queue/jobs/:id*`, adding a job, and\n * `/definitions`), with their query keys.\n *\n * Every key lives under `queryKeys.queue(q)`, so invalidating a queue\n * refreshes its jobs too. Job ids are arbitrary text (up to 1024 characters,\n * `/` and spaces included): each is percent-encoded as ONE path segment.\n */\n\n/** What `GET /queues/:queue/jobs/:id` includes by default (the API's own default, sent explicitly). */\nexport const DEFAULT_JOB_INCLUDE: readonly JobInclude[] = [\n \"data\",\n \"returnValue\",\n \"opts\",\n];\n\n/** States a job does not leave on its own: polling stops there. */\nexport const FINISHED_STATES: ReadonlySet<JobState> = new Set<JobState>([\n \"completed\",\n \"failed\",\n \"dead\",\n]);\n\n/** Whether a job has finished (completed, failed or dead). */\nexport function isFinished(state: JobState): boolean {\n return FINISHED_STATES.has(state);\n}\n\n/** The window and order of one log page. */\nexport interface LogsWindow {\n /** Lines skipped. */\n offset: number;\n /** Page size, at most `limits.maxLogPage`. */\n limit: number;\n /** Oldest (`asc`) or newest (`desc`) first. */\n order: SortOrder;\n}\n\n/** Query keys of one job, all under `[\"queue\", q]`. */\nexport const jobKeys = {\n /** Everything about one job: `[\"queue\", q, \"job\", id]`. */\n job: (queue: string, id: string) =>\n [...queryKeys.queue(queue), \"job\", id] as const,\n /** The job with its stack traces (the lazy section). */\n stacktrace: (queue: string, id: string) =>\n [...jobKeys.job(queue, id), \"stacktrace\"] as const,\n /** Every page of its logs, whatever the window: what clearing them invalidates. */\n logsAll: (queue: string, id: string) =>\n [...jobKeys.job(queue, id), \"logs\"] as const,\n /** One page of its logs. */\n logs: (queue: string, id: string, window: LogsWindow) =>\n [\n ...jobKeys.logsAll(queue, id),\n { offset: window.offset, limit: window.limit, order: window.order },\n ] as const,\n /** Its flow children. */\n children: (queue: string, id: string) =>\n [...jobKeys.job(queue, id), \"children\"] as const,\n /** `GET /definitions`. */\n definitions: () => [\"definitions\"] as const,\n};\n\n/**\n * What every job mutation invalidates: the queue (its lists and this job),\n * every queue list, and the overview.\n */\nexport function mutationInvalidation(queue: string): QueryKey[] {\n return [queryKeys.queue(queue), queryKeys.queuesAll, [\"overview\"]];\n}\n\n/** `/queues/:queue/jobs/:id`, each part percent-encoded. */\nexport function jobPath(queue: string, id: string): string {\n return `/queues/${segment(queue)}/jobs/${segment(id)}`;\n}\n\n/** The app path of a job's screen. */\nexport function jobScreenPath(queue: string, id: string): string {\n return `/queues/${encodeURIComponent(queue)}/jobs/${encodeURIComponent(id)}`;\n}\n\n/** The app path of a queue's screen. */\nexport function queueScreenPath(queue: string): string {\n return `/queues/${encodeURIComponent(queue)}`;\n}\n\n/** Whether a body has what a job screen cannot draw without: string `id`, `name` and `queue`, and a known `state`. */\nexport function isJobShape(fields: Readonly<Record<string, unknown>>): boolean {\n return hasStrings(fields, \"id\", \"name\", \"queue\") && isJobState(fields.state);\n}\n\n/**\n * `GET /queues/:queue/jobs/:id`. A body that is not a job (see\n * {@link isJobShape}) rejects with an `UNEXPECTED_RESPONSE` `ApiError`, so the\n * screen shows its error state rather than an empty job.\n */\nexport async function getJob(\n api: ApiClient,\n queue: string,\n id: string,\n include: readonly JobInclude[] = DEFAULT_JOB_INCLUDE,\n signal?: AbortSignal,\n): Promise<JobDto> {\n const path = jobPath(queue, id);\n const body = await api.request<unknown>(\"GET\", path, {\n query: { include },\n signal,\n });\n return assertShape<JobDto>(body, isJobShape, \"a job\", path);\n}\n\n/** `GET /queues/:queue/jobs/:id/logs`. */\nexport function getJobLogs(\n api: ApiClient,\n queue: string,\n id: string,\n window: LogsWindow,\n signal?: AbortSignal,\n): Promise<LogPageDto> {\n return api.request<LogPageDto>(\"GET\", `${jobPath(queue, id)}/logs`, {\n query: {\n offset: window.offset,\n limit: window.limit,\n order: window.order,\n },\n signal,\n });\n}\n\n/**\n * `DELETE /queues/:queue/jobs/:id/logs`: empties the job's log for good, so\n * its next line is its first again. `removed` is what the log held. 409\n * `JOB_ACTIVE` while a worker runs the job; 404 `JOB_NOT_FOUND`.\n */\nexport function clearJobLogs(\n api: ApiClient,\n queue: string,\n id: string,\n): Promise<ClearJobLogsResultDto> {\n return api.request<ClearJobLogsResultDto>(\n \"DELETE\",\n `${jobPath(queue, id)}/logs`,\n );\n}\n\n/** `GET /queues/:queue/jobs/:id/children`, with no include (the default). */\nexport function getJobChildren(\n api: ApiClient,\n queue: string,\n id: string,\n signal?: AbortSignal,\n): Promise<ChildrenDto> {\n return api.request<ChildrenDto>(\"GET\", `${jobPath(queue, id)}/children`, {\n signal,\n });\n}\n\n/** `PATCH /queues/:queue/jobs/:id`. */\nexport function updateJob(\n api: ApiClient,\n queue: string,\n id: string,\n body: UpdateJobBody,\n): Promise<JobDto> {\n return api.request<JobDto>(\"PATCH\", jobPath(queue, id), { body });\n}\n\n/** `DELETE /queues/:queue/jobs/:id` (204). */\nexport async function removeJob(\n api: ApiClient,\n queue: string,\n id: string,\n): Promise<void> {\n await api.request<undefined>(\"DELETE\", jobPath(queue, id));\n}\n\n/** `POST /queues/:queue/jobs/:id/retry`. */\nexport function retryJob(\n api: ApiClient,\n queue: string,\n id: string,\n resetAttempts: boolean,\n): Promise<RetryJobResultDto> {\n return api.request<RetryJobResultDto>(\"POST\", `${jobPath(queue, id)}/retry`, {\n body: { resetAttempts },\n });\n}\n\n/** `POST /queues/:queue/jobs/:id/promote` (bodiless; the client still sends the JSON content type). */\nexport function promoteJob(\n api: ApiClient,\n queue: string,\n id: string,\n): Promise<PromoteJobResultDto> {\n return api.request<PromoteJobResultDto>(\n \"POST\",\n `${jobPath(queue, id)}/promote`,\n );\n}\n\n/** Longest `reason` `POST /queues/:queue/jobs/:id/fail` accepts (its schema's `maxLength`). */\nexport const FAIL_REASON_MAX_LENGTH = 4096;\n\n/**\n * `POST /queues/:queue/jobs/:id/fail`: the job goes to `dead` with `reason`\n * as its failure, whatever attempts it has left. 409 for a job already\n * completed or dead.\n */\nexport function failJob(\n api: ApiClient,\n queue: string,\n id: string,\n reason: string,\n): Promise<FailJobResultDto> {\n return api.request<FailJobResultDto>(\"POST\", `${jobPath(queue, id)}/fail`, {\n body: { reason },\n });\n}\n\n/** `POST /queues/:queue/jobs`: 201 `added: true`, or 200 `added: false` when the `jobId` already existed. */\nexport function addJob(\n api: ApiClient,\n queue: string,\n body: AddJobBody,\n): Promise<AddJobResultDto> {\n return api.request<AddJobResultDto>(\n \"POST\",\n `/queues/${segment(queue)}/jobs`,\n {\n body,\n },\n );\n}\n\n/**\n * `POST /queues/:queue/flows`: a job and the jobs it waits on, each in its\n * own `queue` or its parent's. 201 when added; 200 `added: false` (with\n * `children: []`) when the top `jobId` already existed, nothing being added\n * below it.\n */\nexport function addFlow(\n api: ApiClient,\n queue: string,\n body: AddFlowBody,\n): Promise<AddFlowResultDto> {\n return api.request<AddFlowResultDto>(\n \"POST\",\n `/queues/${segment(queue)}/flows`,\n { body },\n );\n}\n\n/**\n * What adding a flow invalidates: each queue it wrote to (its jobs lists,\n * counts and detail, all under `[\"queue\", q]`), every queue list, and the\n * overview.\n */\nexport function flowInvalidation(queues: Iterable<string>): QueryKey[] {\n return [\n ...[...new Set(queues)].map((queue) => queryKeys.queue(queue)),\n queryKeys.queuesAll,\n queryKeys.overviewAll,\n ];\n}\n\n/** `GET /definitions`. */\nexport function listDefinitions(\n api: ApiClient,\n signal?: AbortSignal,\n): Promise<DefinitionListDto> {\n return api.request<DefinitionListDto>(\"GET\", \"/definitions\", { signal });\n}\n",
|
|
6
|
+
"import type { AddJobBody, AddJobOptions } from \"../../api/types\";\nimport type { JsonEditorState } from \"../../components/jsonParse\";\nimport { MAX_JOB_ID_LENGTH } from \"@kingsleyweb/bun-jobs/api/contract\";\nimport { jsonEditorState } from \"../../components/jsonParse\";\n\n/** The add-job form's shape, its client-side checks, and its request body. */\n\n/** When the job may first run. */\nexport type AddJobTiming = \"now\" | \"delay\" | \"runAt\";\n\n/** What the add form holds. */\nexport interface AddJobForm {\n /** The job name. */\n name: string;\n /** The payload editor's state. */\n data: JsonEditorState;\n /** A caller-chosen id; `\"\"` lets the queue mint one. */\n jobId: string;\n /** Priority; `undefined` for the default. */\n priority: number | undefined;\n /** Run now, after a delay, or at a time: `delay` and `runAt` are exclusive. */\n timing: AddJobTiming;\n /** The delay in ms, with `timing: \"delay\"`. */\n delay: number | undefined;\n /** The run time (epoch ms), with `timing: \"runAt\"`. */\n runAt: number | undefined;\n /** Attempts allowed in total; `undefined` for the default. */\n attempts: number | undefined;\n /** Fixed delay between attempts, ms; `undefined` for the default. */\n backoff: number | undefined;\n /** Processor timeout, ms; `undefined` for the default. */\n timeout: number | undefined;\n}\n\n/** What {@link emptyAddForm} starts from. */\nexport interface EmptyAddFormOptions {\n /** `MetaDto.addableNames`: the only name is preselected; `null` (any name) or several leave it empty. */\n addableNames: readonly string[] | null;\n /** `limits.maxJobDataBytes`, metered by the data editor. Defaults to no limit. */\n maxBytes?: number;\n}\n\n/** A fresh form: data `{}`, every option at its default, the name preselected when only one is addable. */\nexport function emptyAddForm({\n addableNames,\n maxBytes,\n}: EmptyAddFormOptions): AddJobForm {\n return {\n name: addableNames?.length === 1 ? addableNames[0] : \"\",\n data: jsonEditorState(\"{}\", { maxBytes }),\n jobId: \"\",\n priority: undefined,\n timing: \"now\",\n delay: undefined,\n runAt: undefined,\n attempts: undefined,\n backoff: undefined,\n timeout: undefined,\n };\n}\n\n/** A field of {@link AddJobForm} that can carry an error, keyed as the API's field errors are. */\nexport type AddJobField =\n | \"name\"\n | \"data\"\n | \"opts.jobId\"\n | \"opts.priority\"\n | \"opts.delay\"\n | \"opts.runAt\"\n | \"opts.attempts\"\n | \"opts.backoff\"\n | \"opts.timeout\";\n\n/** Client-side problems, per field. */\nexport type AddJobErrors = Partial<Record<AddJobField, string>>;\n\n/** An integer at least `min`, or `undefined` (left out). */\nfunction checkInteger(\n value: number | undefined,\n min: number,\n): string | undefined {\n if (value === undefined) {\n return undefined;\n }\n if (!Number.isInteger(value)) {\n return \"Enter a whole number.\";\n }\n return value < min ? `Enter ${min} or more.` : undefined;\n}\n\n/** Why a caller-chosen job id would be refused, or `undefined`. */\nexport function checkJobId(jobId: string): string | undefined {\n if (jobId === \"\") {\n return undefined;\n }\n if (jobId.length > MAX_JOB_ID_LENGTH) {\n return `At most ${MAX_JOB_ID_LENGTH} characters (this one has ${jobId.length}).`;\n }\n if (jobId.startsWith(\".\")) {\n return \"An id cannot start with a dot.\";\n }\n // Control characters are refused by bun-jobs' `assertJobId`.\n // eslint-disable-next-line no-control-regex\n if (/[\\u0000-\\u001F\\u007F]/.test(jobId)) {\n return \"An id cannot contain control characters.\";\n }\n return undefined;\n}\n\n/** Checks the form the way the API would. */\nexport function validateAddForm(form: AddJobForm): AddJobErrors {\n const errors: AddJobErrors = {};\n if (form.name.trim() === \"\") {\n errors.name = \"Pick or enter a job name.\";\n } else if (form.name.length > 200) {\n errors.name = \"At most 200 characters.\";\n }\n if (form.data.error) {\n errors.data = \"The data is not valid JSON.\";\n } else if (form.data.overLimit) {\n errors.data = \"The data is larger than the API accepts.\";\n } else if (form.data.value === undefined) {\n errors.data = \"Enter the data as JSON (null is allowed).\";\n }\n const jobId = checkJobId(form.jobId);\n if (jobId) {\n errors[\"opts.jobId\"] = jobId;\n }\n if (form.priority !== undefined && !Number.isFinite(form.priority)) {\n errors[\"opts.priority\"] = \"Enter a number.\";\n }\n if (form.timing === \"delay\") {\n errors[\"opts.delay\"] =\n form.delay === undefined\n ? \"Enter the delay in milliseconds.\"\n : checkInteger(form.delay, 0);\n }\n if (form.timing === \"runAt\" && form.runAt === undefined) {\n errors[\"opts.runAt\"] = \"Pick when it should run.\";\n }\n errors[\"opts.attempts\"] = checkInteger(form.attempts, 1);\n errors[\"opts.backoff\"] = checkInteger(form.backoff, 0);\n errors[\"opts.timeout\"] = checkInteger(form.timeout, 0);\n for (const key of Object.keys(errors) as AddJobField[]) {\n if (errors[key] === undefined) {\n delete errors[key];\n }\n }\n return errors;\n}\n\n/** The request body for a valid {@link AddJobForm}; empty options are left out. */\nexport function addBody(form: AddJobForm): AddJobBody {\n const opts: AddJobOptions = {\n ...(form.jobId === \"\" ? {} : { jobId: form.jobId }),\n ...(form.priority === undefined ? {} : { priority: form.priority }),\n ...(form.timing === \"delay\" && form.delay !== undefined\n ? { delay: form.delay }\n : {}),\n ...(form.timing === \"runAt\" && form.runAt !== undefined\n ? { runAt: form.runAt }\n : {}),\n ...(form.attempts === undefined ? {} : { attempts: form.attempts }),\n ...(form.backoff === undefined ? {} : { backoff: form.backoff }),\n ...(form.timeout === undefined ? {} : { timeout: form.timeout }),\n };\n return {\n name: form.name.trim(),\n data: form.data.value,\n ...(Object.keys(opts).length > 0 ? { opts } : {}),\n };\n}\n",
|
|
7
|
+
"import type { JobState } from \"../../api/types\";\nimport { JOB_STATES } from \"../../api/contract\";\nimport { ApiError, isApiError } from \"../../api/errors\";\nimport { STATE_LABELS } from \"../../format\";\n\n/** A job action whose failures get a plain-language explanation. */\nexport type JobAction =\n \"retry\" | \"promote\" | \"remove\" | \"update\" | \"add\" | \"fail\" | \"clearLogs\";\n\n/** The past participle of each action, for \"cannot be …\". */\nconst VERBS: Readonly<Record<JobAction, string>> = {\n retry: \"retried\",\n promote: \"promoted\",\n remove: \"removed\",\n update: \"updated\",\n add: \"added\",\n fail: \"failed\",\n clearLogs: \"cleared of its logs\",\n};\n\n/** What the action needs, said after a state conflict. */\nconst REQUIREMENTS: Readonly<Record<JobAction, string>> = {\n retry: \"Only a completed, retrying, dead or delayed job can be retried.\",\n promote: \"Only a delayed job can be promoted.\",\n remove: \"Refresh to see where it is now.\",\n update:\n \"A new run time applies only to a waiting or delayed job, and “only in” limits the change to the states you picked.\",\n add: \"\",\n fail: \"Only a job that has not completed or died can be failed.\",\n clearLogs: \"Refresh to see where it is now.\",\n};\n\n/** The job's state from a problem's `context.state`, when it names one. */\nexport function conflictState(error: ApiError): JobState | null {\n const state = error.context.state;\n return typeof state === \"string\" &&\n (JOB_STATES as readonly string[]).includes(state)\n ? (state as JobState)\n : null;\n}\n\n/**\n * The plain-language explanation of a failed job action, or `null` when the\n * API's own detail says it well enough.\n */\nexport function explainJobError(\n error: ApiError,\n action: JobAction,\n): string | null {\n switch (error.code) {\n case \"JOB_ACTIVE\":\n return action === \"clearLogs\"\n ? \"It's running: a worker picked this job up and is still writing its log, so the log cannot be cleared now. Clear it once the job finishes.\"\n : \"It's running: a worker is processing this job right now, so it cannot be removed. Wait for it to finish, then try again.\";\n case \"JOB_STATE_CONFLICT\": {\n const state = conflictState(error);\n if (action === \"fail\" && state === \"active\") {\n // The API buries an active job only under the lock it read it with.\n return \"A worker claimed the job again while it was being failed, so nothing changed. Refresh and try again.\";\n }\n const where = state\n ? `The job is ${STATE_LABELS[state].toLowerCase()} now`\n : \"The job is in another state now\";\n return `${where}, so it cannot be ${VERBS[action]}. ${REQUIREMENTS[action]}`.trim();\n }\n case \"JOB_NOT_FOUND\":\n return \"The job no longer exists: it finished and was cleaned up, or somebody removed it.\";\n case \"NAME_NOT_ADDABLE\": {\n const name = error.context.name;\n return `Jobs named “${typeof name === \"string\" ? name : \"this\"}” may not be added over this API. Pick one of the names it allows.`;\n }\n case \"SERIALIZATION\":\n return \"The data cannot be stored: it is not serialisable for this queue's backend.\";\n default:\n return null;\n }\n}\n\n/**\n * The same error with its detail replaced by the explanation, so a toast or\n * a `ProblemBanner` reads it while keeping the code, status and context.\n * Anything that is not an `ApiError`, or has no better explanation, is\n * returned unchanged.\n */\nexport function friendlyJobError(error: unknown, action: JobAction): unknown {\n if (!isApiError(error)) {\n return error;\n }\n const detail = explainJobError(error, action);\n if (detail === null) {\n return error;\n }\n return new ApiError({\n kind: error.kind,\n status: error.status,\n code: error.code,\n title: error.title,\n detail,\n type: error.type,\n issues: error.issues,\n context: error.context,\n instance: error.instance,\n cause: error,\n });\n}\n\n/** Runs an API call, rethrowing its failure through {@link friendlyJobError}. */\nexport async function withFriendlyErrors<T>(\n action: JobAction,\n run: () => Promise<T>,\n): Promise<T> {\n try {\n return await run();\n } catch (error) {\n throw friendlyJobError(error, action);\n }\n}\n",
|
|
8
|
+
"import type { AddJobField, AddJobForm, AddJobTiming } from \"./addJobForm\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { useState } from \"react\";\nimport { MAX_JOB_ID_LENGTH } from \"../../api/contract\";\nimport { jobKeys, listDefinitions } from \"../../api/jobs\";\nimport { Field } from \"../../components/Field\";\nimport {\n DateTimeInput,\n NumberInput,\n Select,\n TextInput,\n} from \"../../components/inputs\";\nimport { JsonEditor } from \"../../components/JsonEditor\";\nimport { useApiClient } from \"../../context\";\nimport { useCan } from \"../../meta/hooks\";\n\n/**\n * The fields of one job to add, shared by the add-job dialog and each job of\n * the add-flow dialog: its name, its data and its options.\n */\n\n/** The timing choices. */\nconst TIMING_OPTIONS = [\n { value: \"now\", label: \"Now\" },\n { value: \"delay\", label: \"After a delay\" },\n { value: \"runAt\", label: \"At a time\" },\n] as const satisfies readonly { value: AddJobTiming; label: string }[];\n\n/**\n * Select values that cannot be job names in practice (bun-jobs refuses\n * control characters in names' ids, and no sane name starts with NUL):\n * \"nothing picked yet\" and \"type another name\".\n */\nconst PICK_NAME = \"\\u0000pick\";\n/** See {@link PICK_NAME}. */\nconst OTHER_NAME = \"\\u0000other\";\n\n/** Props of {@link JobNameField}. */\nexport interface JobNameFieldProps {\n /** The chosen name. */\n value: string;\n /** Called with a new name. */\n onChange: (name: string) => void;\n /** The error to show under it. */\n error: string | undefined;\n /** `MetaDto.addableNames`: `null` for any name. */\n addableNames: string[] | null;\n}\n\n/**\n * The name picker: the allowed names when the API lists them; otherwise the\n * defined names (from `/definitions`) plus free text, since any name goes.\n */\nexport function JobNameField({\n value,\n onChange,\n error,\n addableNames,\n}: JobNameFieldProps) {\n const api = useApiClient();\n const canDefinitions = useCan(\"definitions.list\");\n const anyName = addableNames === null;\n const definitions = useQuery({\n queryKey: jobKeys.definitions(),\n queryFn: ({ signal }) => listDefinitions(api, signal),\n enabled: anyName && canDefinitions,\n staleTime: 60_000,\n });\n const [typing, setTyping] = useState(false);\n\n if (!anyName) {\n return (\n <Field\n label=\"Name\"\n required\n error={error}\n >\n <Select\n options={[\n { value: \"\", label: \"Pick a name…\", disabled: true },\n ...addableNames.map((name) => ({ value: name })),\n ]}\n value={value}\n onChange={onChange}\n />\n </Field>\n );\n }\n\n const defined = definitions.data?.items.map((item) => item.name) ?? [];\n const freeText = typing || defined.length === 0;\n return (\n <>\n {defined.length > 0 && (\n <Field\n label=\"Name\"\n required\n hint=\"Any name is accepted; the defined ones are listed.\"\n error={freeText ? undefined : error}\n >\n <Select\n options={[\n { value: PICK_NAME, label: \"Pick a name…\", disabled: true },\n ...defined.map((name) => ({ value: name })),\n { value: OTHER_NAME, label: \"Another name…\" },\n ]}\n value={\n freeText\n ? OTHER_NAME\n : defined.includes(value)\n ? value\n : PICK_NAME\n }\n onChange={(chosen) => {\n if (chosen === OTHER_NAME) {\n setTyping(true);\n onChange(\"\");\n } else {\n setTyping(false);\n onChange(chosen);\n }\n }}\n />\n </Field>\n )}\n {freeText && (\n <Field\n label={defined.length > 0 ? \"Another name\" : \"Name\"}\n required\n hint={\n defined.length > 0\n ? undefined\n : \"Any name is accepted. A worker must handle it for the job to run.\"\n }\n error={error}\n >\n <TextInput\n value={value}\n onChange={onChange}\n maxLength={200}\n autoComplete=\"off\"\n spellCheck={false}\n />\n </Field>\n )}\n </>\n );\n}\n\n/** Props of {@link JobDataField}. */\nexport interface JobDataFieldProps {\n /** The editor's text. */\n value: string;\n /** Called with the editor's new state. */\n onChange: (data: AddJobForm[\"data\"]) => void;\n /** The error to show under it. */\n error: string | undefined;\n /** `limits.maxJobDataBytes`, metered as the data is typed. */\n maxBytes: number;\n /** Visible rows. Defaults to 8. */\n rows?: number;\n}\n\n/** The job's data, as JSON (`null` allowed). */\nexport function JobDataField({\n value,\n onChange,\n error,\n maxBytes,\n rows = 8,\n}: JobDataFieldProps) {\n return (\n <Field\n label=\"Data\"\n required\n hint=\"JSON; null is allowed.\"\n error={error}\n >\n <JsonEditor\n value={value}\n onChange={onChange}\n maxBytes={maxBytes}\n rows={rows}\n />\n </Field>\n );\n}\n\n/** Props of {@link JobOptionsFields}. */\nexport interface JobOptionsFieldsProps {\n /** The form whose options are shown. */\n form: AddJobForm;\n /** Called with the fields that changed. */\n patch: (next: Partial<AddJobForm>) => void;\n /** The error to show under a field, if any. */\n fieldError: (field: AddJobField) => string | undefined;\n /** The run-time input's own problem (out of range, unparseable), or `undefined` once it has none. */\n onRunAtProblem: (problem: string | undefined) => void;\n}\n\n/** The options `POST /queues/:queue/jobs` accepts, in a fieldset of their own. */\nexport function JobOptionsFields({\n form,\n patch,\n fieldError,\n onRunAtProblem,\n}: JobOptionsFieldsProps) {\n return (\n <fieldset className=\"job-form-options\">\n <legend>Options</legend>\n <Field\n label=\"Job id\"\n hint={`Optional, at most ${MAX_JOB_ID_LENGTH} characters. An id that already exists returns that job instead.`}\n error={fieldError(\"opts.jobId\")}\n >\n <TextInput\n value={form.jobId}\n onChange={(jobId) => patch({ jobId })}\n maxLength={MAX_JOB_ID_LENGTH}\n autoComplete=\"off\"\n spellCheck={false}\n />\n </Field>\n <Field\n label=\"Priority\"\n hint=\"Lower runs first.\"\n error={fieldError(\"opts.priority\")}\n >\n <NumberInput\n value={form.priority}\n onChange={(priority) => patch({ priority })}\n step=\"any\"\n />\n </Field>\n <Field label=\"Run\">\n <Select\n options={TIMING_OPTIONS}\n value={form.timing}\n onChange={(timing) => patch({ timing })}\n />\n </Field>\n {form.timing === \"delay\" && (\n <Field\n label=\"Delay (ms)\"\n required\n error={fieldError(\"opts.delay\")}\n >\n <NumberInput\n value={form.delay}\n onChange={(delay) => patch({ delay })}\n min={0}\n />\n </Field>\n )}\n {form.timing === \"runAt\" && (\n <Field\n label=\"Run at\"\n required\n error={fieldError(\"opts.runAt\")}\n >\n <DateTimeInput\n value={form.runAt}\n onChange={(runAt) => patch({ runAt })}\n onProblem={onRunAtProblem}\n />\n </Field>\n )}\n <Field\n label=\"Attempts\"\n hint=\"Attempts allowed in total, 1 or more.\"\n error={fieldError(\"opts.attempts\")}\n >\n <NumberInput\n value={form.attempts}\n onChange={(attempts) => patch({ attempts })}\n min={1}\n />\n </Field>\n <Field\n label=\"Backoff (ms)\"\n hint=\"Fixed delay between attempts.\"\n error={fieldError(\"opts.backoff\")}\n >\n <NumberInput\n value={form.backoff}\n onChange={(backoff) => patch({ backoff })}\n min={0}\n />\n </Field>\n <Field\n label=\"Timeout (ms)\"\n hint=\"How long one attempt may run.\"\n error={fieldError(\"opts.timeout\")}\n >\n <NumberInput\n value={form.timeout}\n onChange={(timeout) => patch({ timeout })}\n min={0}\n />\n </Field>\n </fieldset>\n );\n}\n"
|
|
9
|
+
],
|
|
10
|
+
"mappings": "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",
|
|
11
|
+
"debugId": "BDEFEAB68BC650DF64756E2164756E21",
|
|
12
|
+
"names": []
|
|
13
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{r,N,n,e,t}from"./main-xf44bv26.js";import{na,ye}from"./main-3xxhy5jh.js";function S(s){if(s===null)return"null";if(Array.isArray(s))return"array";switch(typeof s){case"object":return s instanceof Date?"other":"object";case"string":return"string";case"number":case"bigint":return"number";case"boolean":return"boolean";case"undefined":return"undefined";default:return"other"}}function C(s){if(typeof s==="bigint")return`${s}n`;if(s instanceof Date)return Number.isNaN(s.getTime())?"Invalid Date":s.toISOString();if(typeof s==="symbol"||typeof s==="function")return s.toString();return String(s)}function J(s,a){if(Array.isArray(s))return s.slice(0,a).map((i,u)=>[String(u),i]);let c=s;return Object.keys(c).slice(0,a).map((i)=>[i,c[i]])}function ie({value:s,label:a="JSON",expandDepth:c=1,maxStringLength:i=200,maxChildren:u=100,copyable:o=!0,className:l}){let m={expandDepth:c,maxStringLength:i,maxChildren:u};return t("div",{className:N("json-view",l),children:[o&&e("div",{className:"json-view-toolbar",children:e(ye,{text:()=>na(s,2),ariaLabel:`Copy ${a}`})}),e("ul",{className:"json-tree","aria-label":a,children:e(g,{name:null,value:s,depth:0,ancestors:[],limits:m})})]})}function g({name:s,value:a,depth:c,ancestors:i,limits:u}){let o=S(a),l=s===null?null:t("span",{className:"json-key",children:[s,": "]});if(o!=="object"&&o!=="array")return t("li",{className:"json-row",children:[l,e(P,{value:a,kind:o,maxLength:u.maxStringLength})]});let m=a;if(i.includes(m))return t("li",{className:"json-row",children:[l,e("span",{className:"json-circular",children:"[Circular]"})]});return e(L,{nameNode:l,value:m,isArray:o==="array",depth:c,ancestors:i,limits:u})}function L({nameNode:s,value:a,isArray:c,depth:i,ancestors:u,limits:o}){let[l,m]=r(i<o.expandDepth),[b,j]=r(o.maxChildren),p=c?a.length:Object.keys(a).length,h=c?"[":"{",f=c?"]":"}",y=c?`${p} ${p===1?"item":"items"}`:`${p} ${p===1?"key":"keys"}`;if(p===0)return t("li",{className:"json-row",children:[s,t("span",{className:"json-punct",children:[h,f]})]});let w=l?J(a,b):[],x=l?[...u,a]:u;return t("li",{className:"json-row json-container",children:[t("button",{type:"button",className:"json-toggle","aria-expanded":l,onClick:()=>m((d)=>!d),children:[e("span",{className:"json-caret","aria-hidden":"true"}),s,e("span",{className:"json-punct",children:h}),!l&&t(n,{children:[e("span",{className:"json-summary",children:y}),e("span",{className:"json-punct",children:f})]})]}),l&&t(n,{children:[t("ul",{className:"json-children",children:[w.map(([d,k])=>e(g,{name:d,value:k,depth:i+1,ancestors:x,limits:o},d)),p>b&&e("li",{className:"json-row",children:t("button",{type:"button",className:"json-more",onClick:()=>j((d)=>d+o.maxChildren),children:["Show ",Math.min(o.maxChildren,p-b)," more (",p-b," hidden)"]})})]}),e("span",{className:"json-punct",children:f})]})]})}function P({value:s,kind:a,maxLength:c}){let[i,u]=r(!1);if(a==="string"){let o=s,l=!i&&o.length>c;return t(n,{children:[t("span",{className:"json-string",children:['"',l?o.slice(0,c):o,l?"…":"",'"']}),o.length>c&&e("button",{type:"button",className:"json-more","aria-expanded":i,onClick:()=>u((m)=>!m),children:i?"Show less":`Show more (${o.length} chars)`})]})}return e("span",{className:`json-${a}`,children:C(s)})}
|
|
2
|
+
export{ie};
|
|
3
|
+
|
|
4
|
+
//# debugId=D382AD4917856C5764756E2164756E21
|
|
5
|
+
//# sourceMappingURL=main-qahvywvw.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["app/jobs/components/JsonView.tsx"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { ReactNode } from \"react\";\nimport { useState } from \"react\";\nimport { cx } from \"./classNames\";\nimport { safeStringify } from \"./copy\";\nimport { CopyButton } from \"./CopyButton\";\n\n/** Limits of a {@link JsonView}. */\ninterface ViewLimits {\n /** Levels expanded on first render. */\n expandDepth: number;\n /** Characters of a string shown before \"show more\". */\n maxStringLength: number;\n /** Children rendered per expand (and per \"show more\"). */\n maxChildren: number;\n}\n\n/** Props of {@link JsonView}. */\nexport interface JsonViewProps {\n /** Any value: JSON, `undefined`, a `Date`, a `bigint`, even a circular object. */\n value: unknown;\n /** Accessible name of the tree, e.g. \"Job data\". Defaults to `\"JSON\"`. */\n label?: string;\n /** Levels expanded on first render; `0` starts collapsed. Defaults to `1`. */\n expandDepth?: number;\n /** Characters of a string shown before a \"show more\" control. Defaults to `200`. */\n maxStringLength?: number;\n /** Array items / object keys rendered per expand; \"show more\" renders the next batch. Defaults to `100`. */\n maxChildren?: number;\n /** Show a Copy button for the whole value (serialised on click, circular-safe). Defaults to `true`. */\n copyable?: boolean;\n /** Extra class names. */\n className?: string;\n}\n\n/** What a value looks like to the viewer. */\ntype Kind =\n | \"object\"\n | \"array\"\n | \"string\"\n | \"number\"\n | \"boolean\"\n | \"null\"\n | \"undefined\"\n | \"other\";\n\n/** Classifies a value for rendering. */\nfunction kindOf(value: unknown): Kind {\n if (value === null) {\n return \"null\";\n }\n if (Array.isArray(value)) {\n return \"array\";\n }\n switch (typeof value) {\n case \"object\":\n return value instanceof Date ? \"other\" : \"object\";\n case \"string\":\n return \"string\";\n case \"number\":\n case \"bigint\":\n return \"number\";\n case \"boolean\":\n return \"boolean\";\n case \"undefined\":\n return \"undefined\";\n default:\n return \"other\";\n }\n}\n\n/** A primitive's (or opaque value's) text. */\nfunction scalarText(value: unknown): string {\n if (typeof value === \"bigint\") {\n return `${value}n`;\n }\n if (value instanceof Date) {\n return Number.isNaN(value.getTime()) ? \"Invalid Date\" : value.toISOString();\n }\n if (typeof value === \"symbol\" || typeof value === \"function\") {\n return value.toString();\n }\n return String(value);\n}\n\n/** The first `limit` entries of a container, arrays keyed by index; never touches the rest. */\nfunction entriesOf(value: object, limit: number): [string, unknown][] {\n if (Array.isArray(value)) {\n return value\n .slice(0, limit)\n .map((item: unknown, index) => [String(index), item]);\n }\n const record = value as Record<string, unknown>;\n return Object.keys(record)\n .slice(0, limit)\n .map((key) => [key, record[key]]);\n}\n\n/**\n * A read-only, collapsible tree of any value. Built for job payloads of any\n * size: levels past `expandDepth` start collapsed, each expand renders at\n * most `maxChildren` children (with \"show more\"), long strings are cut at\n * `maxStringLength`, and a reference back to an ancestor renders as\n * `[Circular]` instead of recursing.\n */\nexport function JsonView({\n value,\n label = \"JSON\",\n expandDepth = 1,\n maxStringLength = 200,\n maxChildren = 100,\n copyable = true,\n className,\n}: JsonViewProps) {\n const limits: ViewLimits = { expandDepth, maxStringLength, maxChildren };\n return (\n <div className={cx(\"json-view\", className)}>\n {copyable && (\n <div className=\"json-view-toolbar\">\n <CopyButton\n text={() => safeStringify(value, 2)}\n ariaLabel={`Copy ${label}`}\n />\n </div>\n )}\n <ul\n className=\"json-tree\"\n aria-label={label}\n >\n <JsonNode\n name={null}\n value={value}\n depth={0}\n ancestors={[]}\n limits={limits}\n />\n </ul>\n </div>\n );\n}\n\n/** Props of {@link JsonNode}. */\ninterface JsonNodeProps {\n /** The key or index, or `null` at the root. */\n name: string | null;\n /** The value at this node. */\n value: unknown;\n /** Nesting level, root = 0. */\n depth: number;\n /** The containers above this node, for the circular check. */\n ancestors: readonly object[];\n /** Rendering limits. */\n limits: ViewLimits;\n}\n\n/** One key/value row, expandable when the value is a non-empty container. */\nfunction JsonNode({ name, value, depth, ancestors, limits }: JsonNodeProps) {\n const kind = kindOf(value);\n const key = name === null ? null : <span className=\"json-key\">{name}: </span>;\n\n if (kind !== \"object\" && kind !== \"array\") {\n return (\n <li className=\"json-row\">\n {key}\n <JsonScalar\n value={value}\n kind={kind}\n maxLength={limits.maxStringLength}\n />\n </li>\n );\n }\n\n const container = value as object;\n if (ancestors.includes(container)) {\n return (\n <li className=\"json-row\">\n {key}\n <span className=\"json-circular\">[Circular]</span>\n </li>\n );\n }\n return (\n <JsonContainer\n nameNode={key}\n value={container}\n isArray={kind === \"array\"}\n depth={depth}\n ancestors={ancestors}\n limits={limits}\n />\n );\n}\n\n/** Props of {@link JsonContainer}. */\ninterface JsonContainerProps {\n /** The rendered key, or `null` at the root. */\n nameNode: ReactNode;\n /** The object or array. */\n value: object;\n /** Whether it is an array. */\n isArray: boolean;\n /** Nesting level. */\n depth: number;\n /** The containers above it. */\n ancestors: readonly object[];\n /** Rendering limits. */\n limits: ViewLimits;\n}\n\n/** An object or array: a disclosure button, and its children once expanded. */\nfunction JsonContainer({\n nameNode,\n value,\n isArray,\n depth,\n ancestors,\n limits,\n}: JsonContainerProps) {\n const [expanded, setExpanded] = useState(depth < limits.expandDepth);\n const [shown, setShown] = useState(limits.maxChildren);\n // Object.keys is O(n) but cheap next to rendering; children are only\n // materialised while expanded.\n const size = isArray\n ? (value as unknown[]).length\n : Object.keys(value).length;\n const open = isArray ? \"[\" : \"{\";\n const close = isArray ? \"]\" : \"}\";\n const summary = isArray\n ? `${size} ${size === 1 ? \"item\" : \"items\"}`\n : `${size} ${size === 1 ? \"key\" : \"keys\"}`;\n\n if (size === 0) {\n return (\n <li className=\"json-row\">\n {nameNode}\n <span className=\"json-punct\">\n {open}\n {close}\n </span>\n </li>\n );\n }\n\n const children = expanded ? entriesOf(value, shown) : [];\n const nextAncestors = expanded ? [...ancestors, value] : ancestors;\n return (\n <li className=\"json-row json-container\">\n <button\n type=\"button\"\n className=\"json-toggle\"\n aria-expanded={expanded}\n onClick={() => setExpanded((current) => !current)}\n >\n <span\n className=\"json-caret\"\n aria-hidden=\"true\"\n />\n {nameNode}\n <span className=\"json-punct\">{open}</span>\n {!expanded && (\n <>\n <span className=\"json-summary\">{summary}</span>\n <span className=\"json-punct\">{close}</span>\n </>\n )}\n </button>\n {expanded && (\n <>\n <ul className=\"json-children\">\n {children.map(([key, item]) => (\n <JsonNode\n key={key}\n name={key}\n value={item}\n depth={depth + 1}\n ancestors={nextAncestors}\n limits={limits}\n />\n ))}\n {size > shown && (\n <li className=\"json-row\">\n <button\n type=\"button\"\n className=\"json-more\"\n onClick={() =>\n setShown((current) => current + limits.maxChildren)\n }\n >\n Show {Math.min(limits.maxChildren, size - shown)} more (\n {size - shown} hidden)\n </button>\n </li>\n )}\n </ul>\n <span className=\"json-punct\">{close}</span>\n </>\n )}\n </li>\n );\n}\n\n/** Props of {@link JsonScalar}. */\ninterface JsonScalarProps {\n /** The value. */\n value: unknown;\n /** Its kind. */\n kind: Kind;\n /** String length before truncation. */\n maxLength: number;\n}\n\n/** A leaf value, colour-coded; long strings truncate with \"show more\". */\nfunction JsonScalar({ value, kind, maxLength }: JsonScalarProps) {\n const [full, setFull] = useState(false);\n if (kind === \"string\") {\n const text = value as string;\n const cut = !full && text.length > maxLength;\n return (\n <>\n <span className=\"json-string\">\n "{cut ? text.slice(0, maxLength) : text}\n {cut ? \"…\" : \"\"}"\n </span>\n {text.length > maxLength && (\n <button\n type=\"button\"\n className=\"json-more\"\n aria-expanded={full}\n onClick={() => setFull((current) => !current)}\n >\n {full ? \"Show less\" : `Show more (${text.length} chars)`}\n </button>\n )}\n </>\n );\n }\n return <span className={`json-${kind}`}>{scalarText(value)}</span>;\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "D382AD4917856C5764756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import{ve,N,e}from"./main-xf44bv26.js";var u=1e4,s=new Set,l=Date.now(),a=null;function w(){l=Date.now();for(let n of s)n()}function S(n){if(s.size===0)l=Date.now(),a=setInterval(w,u);return s.add(n),()=>{if(s.delete(n),s.size===0&&a!==null)clearInterval(a),a=null}}function p(){return l}function Ie(){return ve(S,p,p)}var T=[["second",1],["minute",60],["hour",3600],["day",86400],["week",604800],["month",2629800],["year",31557600]],y={second:45,minute:45,hour:22,day:6,week:4,month:11},b=new Map;function g(n){let r=n??"",t=b.get(r);if(!t)t=new Intl.RelativeTimeFormat(n,{numeric:"auto",style:"narrow"}),b.set(r,t);return t}var I=u/1000;function _(n,r,t){let o=(n-r)/1000,i=o>0&&o<=I?0:o,h=Math.abs(i);for(let m=0;m<T.length;m++){let[c,f]=T[m],d=y[c];if(d===void 0||h/f<d)return g(t).format(Math.round(i/f),c)}return g(t).format(Math.round(i/31557600),"year")}function R(n){if(n===null||n===void 0||n==="")return null;let r=n instanceof Date?n.getTime():typeof n==="number"?n:Date.parse(n);return Number.isFinite(r)?r:null}function v({value:n,fallback:r="—",className:t,hint:o}){let i=R(n);if(i===null)return e("span",{className:N("relative-time",t),children:r});return e(k,{ms:i,className:t,hint:o})}function k({ms:n,className:r,hint:t}){let o=Ie(),i=new Date(n).toISOString();return e("time",{className:N("relative-time",r),dateTime:i,title:t===void 0?i:`${i}
|
|
2
|
+
${t}`,children:_(n,o)})}
|
|
3
|
+
export{Ie,v};
|
|
4
|
+
|
|
5
|
+
//# debugId=8436A83131106A2464756E2164756E21
|
|
6
|
+
//# sourceMappingURL=main-smabxbn4.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["app/jobs/hooks/useNow.ts", "app/jobs/components/formatRelative.ts", "app/jobs/components/RelativeTime.tsx"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { useSyncExternalStore } from \"react\";\n\n/**\n * One app-wide clock for relative times. However many components show\n * \"3m ago\", there is a single `setInterval`, started with the first\n * subscriber and stopped with the last, so a table of 500 rows does not\n * run 500 timers.\n */\n\n/** How often the shared clock ticks, ms. */\nexport const NOW_TICK_MS = 10_000;\n\n/** Subscribers to the clock. */\nconst listeners = new Set<() => void>();\n/** The time every subscriber reads, updated on each tick. */\nlet now = Date.now();\n/** The one interval, while anything subscribes. */\nlet timer: ReturnType<typeof setInterval> | null = null;\n\n/** Advances the shared time and tells every subscriber. */\nfunction tick(): void {\n now = Date.now();\n for (const listener of listeners) {\n listener();\n }\n}\n\n/** Subscribes to the clock; starts it for the first subscriber, stops it after the last. */\nfunction subscribe(listener: () => void): () => void {\n if (listeners.size === 0) {\n // The clock was idle: refresh before anyone reads a stale time.\n now = Date.now();\n timer = setInterval(tick, NOW_TICK_MS);\n }\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n if (listeners.size === 0 && timer !== null) {\n clearInterval(timer);\n timer = null;\n }\n };\n}\n\n/** The current shared time. */\nfunction getSnapshot(): number {\n return now;\n}\n\n/** The current time, epoch ms, re-rendering every {@link NOW_TICK_MS} on the shared clock. */\nexport function useNow(): number {\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/** How many components are on the shared clock, and whether its timer runs. For tests. */\nexport function sharedClockState(): { subscribers: number; running: boolean } {\n return { subscribers: listeners.size, running: timer !== null };\n}\n",
|
|
6
|
+
"import { NOW_TICK_MS } from \"../hooks/useNow\";\n\n/** Units from the smallest up, with how many seconds each spans. */\nconst UNITS: readonly [Intl.RelativeTimeFormatUnit, number][] = [\n [\"second\", 1],\n [\"minute\", 60],\n [\"hour\", 3_600],\n [\"day\", 86_400],\n [\"week\", 604_800],\n [\"month\", 2_629_800],\n [\"year\", 31_557_600],\n];\n\n/** When to switch to the next unit: 45 s → minutes, 45 min → hours, 22 h → days, 6 d → weeks, 4 w → months, 11 mo → years. */\nconst THRESHOLDS: Readonly<\n Partial<Record<Intl.RelativeTimeFormatUnit, number>>\n> = {\n second: 45,\n minute: 45,\n hour: 22,\n day: 6,\n week: 4,\n month: 11,\n};\n\n/** Formatters by locale, built once. */\nconst formatters = new Map<string, Intl.RelativeTimeFormat>();\n\n/** A narrow, `numeric: \"auto\"` formatter for a locale (\"3m ago\", \"in 2h\", \"now\", \"yesterday\"). */\nfunction formatter(locale: string | undefined): Intl.RelativeTimeFormat {\n const key = locale ?? \"\";\n let found = formatters.get(key);\n if (!found) {\n found = new Intl.RelativeTimeFormat(locale, {\n numeric: \"auto\",\n style: \"narrow\",\n });\n formatters.set(key, found);\n }\n return found;\n}\n\n/**\n * How far ahead of `now` an instant may be and still read \"now\": one tick of\n * the shared clock. Every relative time in the app is rendered against that\n * cached clock, so anything recorded since its last tick is legitimately\n * ahead of it — a worker's heartbeat written a moment ago rendered as\n * \"in 2s\" until the clock caught up. Beyond a tick it is a real future time\n * (a delayed job, a next run) and is shown as one.\n */\nconst FUTURE_TOLERANCE_S = NOW_TICK_MS / 1_000;\n\n/**\n * `ms` relative to `now`: \"3m ago\", \"in 2h\", \"now\", \"yesterday\". The unit\n * is the largest that keeps the number at or above 1. An instant up to\n * {@link FUTURE_TOLERANCE_S} ahead reads \"now\".\n */\nexport function formatRelativeTime(\n ms: number,\n now: number,\n locale?: string,\n): string {\n const ahead = (ms - now) / 1000;\n const seconds = ahead > 0 && ahead <= FUTURE_TOLERANCE_S ? 0 : ahead;\n const abs = Math.abs(seconds);\n for (let i = 0; i < UNITS.length; i++) {\n const [unit, span] = UNITS[i];\n const limit = THRESHOLDS[unit];\n if (limit === undefined || abs / span < limit) {\n return formatter(locale).format(Math.round(seconds / span), unit);\n }\n }\n return formatter(locale).format(Math.round(seconds / 31_557_600), \"year\");\n}\n\n/** Normalises a time input to epoch ms; `null` for missing or invalid. */\nexport function toEpochMs(\n value: number | string | Date | null | undefined,\n): number | null {\n if (value === null || value === undefined || value === \"\") {\n return null;\n }\n const ms =\n value instanceof Date\n ? value.getTime()\n : typeof value === \"number\"\n ? value\n : Date.parse(value);\n return Number.isFinite(ms) ? ms : null;\n}\n",
|
|
7
|
+
"import { useNow } from \"../hooks/useNow\";\nimport { cx } from \"./classNames\";\nimport { formatRelativeTime, toEpochMs } from \"./formatRelative\";\n\n/** Props of {@link RelativeTime}. */\nexport interface RelativeTimeProps {\n /** The instant: epoch ms, an ISO string or a `Date`. `null`/`undefined` (or invalid) renders \"—\". */\n value: number | string | Date | null | undefined;\n /** What to render for a missing value. Defaults to `\"—\"`. */\n fallback?: string;\n /** Extra class names. */\n className?: string;\n /**\n * A note appended to the `<time>`'s tooltip, on its own line under the\n * absolute instant — so a caller can explain the instant without taking\n * the tooltip away from it. Absent leaves the tooltip the instant alone,\n * and it is ignored for a missing value (there is no `<time>` then).\n */\n hint?: string;\n}\n\n/**\n * \"3m ago\" / \"in 2h\", kept current by the one shared clock (`useNow`),\n * inside a `<time>` whose `dateTime` and `title` carry the absolute ISO\n * instant, so hovering shows exactly when.\n */\nexport function RelativeTime({\n value,\n fallback = \"—\",\n className,\n hint,\n}: RelativeTimeProps) {\n const ms = toEpochMs(value);\n if (ms === null) {\n return <span className={cx(\"relative-time\", className)}>{fallback}</span>;\n }\n return (\n <TickingTime\n ms={ms}\n className={className}\n hint={hint}\n />\n );\n}\n\n/** Props of {@link TickingTime}. */\ninterface TickingTimeProps {\n /** The instant, epoch ms. */\n ms: number;\n /** Extra class names. */\n className?: string;\n /** A note under the instant in the tooltip. See {@link RelativeTimeProps.hint}. */\n hint?: string;\n}\n\n/** The subscribed part, so a missing value never joins the clock. */\nfunction TickingTime({ ms, className, hint }: TickingTimeProps) {\n const now = useNow();\n const iso = new Date(ms).toISOString();\n return (\n <time\n className={cx(\"relative-time\", className)}\n dateTime={iso}\n title={hint === undefined ? iso : `${iso}\\n${hint}`}\n >\n {formatRelativeTime(ms, now)}\n </time>\n );\n}\n"
|
|
8
|
+
],
|
|
9
|
+
"mappings": "uCAUO,IAAM,EAAc,IAGrB,EAAY,IAAI,IAElB,EAAM,KAAK,IAAI,EAEf,EAA+C,KAGnD,SAAS,CAAI,EAAS,CACpB,EAAM,KAAK,IAAI,EACf,QAAW,KAAY,EACrB,EAAS,EAKb,SAAS,CAAS,CAAC,EAAkC,CACnD,GAAI,EAAU,OAAS,EAErB,EAAM,KAAK,IAAI,EACf,EAAQ,YAAY,EAAM,CAAW,EAGvC,OADA,EAAU,IAAI,CAAQ,EACf,IAAM,CAEX,GADA,EAAU,OAAO,CAAQ,EACrB,EAAU,OAAS,GAAK,IAAU,KACpC,cAAc,CAAK,EACnB,EAAQ,MAMd,SAAS,CAAW,EAAW,CAC7B,OAAO,EAIF,SAAS,EAAM,EAAW,CAC/B,OAAO,GAAqB,EAAW,EAAa,CAAW,EChDjE,IAAM,EAA0D,CAC9D,CAAC,SAAU,CAAC,EACZ,CAAC,SAAU,EAAE,EACb,CAAC,OAAQ,IAAK,EACd,CAAC,MAAO,KAAM,EACd,CAAC,OAAQ,MAAO,EAChB,CAAC,QAAS,OAAS,EACnB,CAAC,OAAQ,QAAU,CACrB,EAGM,EAEF,CACF,OAAQ,GACR,OAAQ,GACR,KAAM,GACN,IAAK,EACL,KAAM,EACN,MAAO,EACT,EAGM,EAAa,IAAI,IAGvB,SAAS,CAAS,CAAC,EAAqD,CACtE,IAAM,EAAM,GAAU,GAClB,EAAQ,EAAW,IAAI,CAAG,EAC9B,GAAI,CAAC,EACH,EAAQ,IAAI,KAAK,mBAAmB,EAAQ,CAC1C,QAAS,OACT,MAAO,QACT,CAAC,EACD,EAAW,IAAI,EAAK,CAAK,EAE3B,OAAO,EAWT,IAAM,EAAqB,EAAc,KAOlC,SAAS,CAAkB,CAChC,EACA,EACA,EACQ,CACR,IAAM,GAAS,EAAK,GAAO,KACrB,EAAU,EAAQ,GAAK,GAAS,EAAqB,EAAI,EACzD,EAAM,KAAK,IAAI,CAAO,EAC5B,QAAS,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACrC,IAAO,EAAM,GAAQ,EAAM,GACrB,EAAQ,EAAW,GACzB,GAAI,IAAU,QAAa,EAAM,EAAO,EACtC,OAAO,EAAU,CAAM,EAAE,OAAO,KAAK,MAAM,EAAU,CAAI,EAAG,CAAI,EAGpE,OAAO,EAAU,CAAM,EAAE,OAAO,KAAK,MAAM,EAAU,QAAU,EAAG,MAAM,EAInE,SAAS,CAAS,CACvB,EACe,CACf,GAAI,IAAU,MAAQ,IAAU,QAAa,IAAU,GACrD,OAAO,KAET,IAAM,EACJ,aAAiB,KACb,EAAM,QAAQ,EACd,OAAO,IAAU,SACf,EACA,KAAK,MAAM,CAAK,EACxB,OAAO,OAAO,SAAS,CAAE,EAAI,EAAK,KC9D7B,SAAS,CAAY,EAC1B,QACA,WAAW,IACX,YACA,QACoB,CACpB,IAAM,EAAK,EAAU,CAAK,EAC1B,GAAI,IAAO,KACT,OAAO,EAAC,OAAD,CAAM,UAAW,EAAG,gBAAiB,CAAS,EAA9C,SAAkD,EAAW,EAEtE,OACE,EAAC,EAAD,CACE,GAAI,EACJ,UAAW,EACX,KAAM,EACR,EAeJ,SAAS,CAAW,EAAG,KAAI,YAAW,QAA0B,CAC9D,IAAM,EAAM,GAAO,EACb,EAAM,IAAI,KAAK,CAAE,EAAE,YAAY,EACrC,OACE,EAAC,OAAD,CACE,UAAW,EAAG,gBAAiB,CAAS,EACxC,SAAU,EACV,MAAO,IAAS,OAAY,EAAM,GAAG;AAAA,EAAQ,IAH/C,SAKG,EAAmB,EAAI,CAAG,EAC3B",
|
|
10
|
+
"debugId": "8436A83131106A2464756E2164756E21",
|
|
11
|
+
"names": []
|
|
12
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{T,r,n,e,t,a,oe,u,M}from"./main-xf44bv26.js";import{A}from"./main-9qd3nqs5.js";function k(i){return i.open?e(z,{...i}):null}function z({onClose:i,title:E,description:h,children:B,onConfirm:L,confirmLabel:d="Confirm",pendingLabel:F,cancelLabel:N="Cancel",variant:P="default",confirmText:l,confirmTextLabel:H,closeOnSuccess:O=!0,confirmDisabled:S=!1}){let[m,w]=r(""),[o,c]=r(!1),[p,g]=r(null),b=T(null),C=T(null),R=T(null),v=(l===void 0||m===l)&&!o&&!S,s=P==="danger",y=async()=>{if(!v)return;c(!0),g(null);try{if(await L(),c(!1),O)i()}catch(f){c(!1),g(f)}},I=(f)=>{f.preventDefault(),y()},D=()=>{if(!o)i()};return e(oe,{open:!0,onClose:D,title:E,description:h,role:s?"alertdialog":"dialog",size:"sm",closeOnEscape:!o,closeOnBackdrop:!o,initialFocusRef:l!==void 0?R:s?b:C,footer:t(n,{children:[e(a,{ref:b,onClick:D,disabled:o,children:N}),e(a,{ref:C,variant:s?"danger":"primary",disabled:!v,"aria-busy":o||void 0,onClick:()=>void y(),children:o?F??`${d}…`:d})]}),children:t("form",{className:"confirm-dialog-body",onSubmit:I,children:[B,l!==void 0&&e(u,{label:H??t(n,{children:["Type ",e("code",{children:l})," to confirm"]}),children:e(M,{ref:R,value:m,onChange:w,autoComplete:"off",spellCheck:!1,disabled:o})}),p!==null&&e(A,{error:p})]})})}
|
|
2
|
+
export{k};
|
|
3
|
+
|
|
4
|
+
//# debugId=01B35119F2CD3AC464756E2164756E21
|
|
5
|
+
//# sourceMappingURL=main-v2f8a4g3.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["app/jobs/components/ConfirmDialog.tsx"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { FormEvent, ReactNode } from \"react\";\nimport { useRef, useState } from \"react\";\nimport { Button } from \"./Button\";\nimport { Dialog } from \"./Dialog\";\nimport { Field } from \"./Field\";\nimport { TextInput } from \"./inputs\";\nimport { ProblemBanner } from \"./ProblemBanner\";\n\n/** Props of {@link ConfirmDialog}. */\nexport interface ConfirmDialogProps {\n /** Whether it is showing. State (typed text, error) resets on each opening. */\n open: boolean;\n /** Asks to close (Cancel, Escape, backdrop, or after a successful confirm). Ignored while pending. */\n onClose: () => void;\n /** The question, e.g. \"Drain queue emails?\". */\n title: ReactNode;\n /** What will happen, in a sentence. */\n description?: ReactNode;\n /** Extra body content, e.g. options such as a \"include delayed\" checkbox. */\n children?: ReactNode;\n /**\n * The action. May be async: the dialog shows a pending state until it\n * settles, closes on success, and on a throw stays open with the error\n * shown inline (an `ApiError` as a {@link ProblemBanner}).\n */\n onConfirm: () => unknown;\n /** The confirm button's text. Defaults to `\"Confirm\"`. */\n confirmLabel?: string;\n /** The confirm button's text while pending. Defaults to `confirmLabel` + \"…\". */\n pendingLabel?: string;\n /** The cancel button's text. Defaults to `\"Cancel\"`. */\n cancelLabel?: string;\n /** `\"danger\"` for destructive actions: a red button, `alertdialog`, and Cancel focused first. Defaults to `\"default\"`. */\n variant?: \"default\" | \"danger\";\n /** When set, the user must type exactly this (e.g. the queue name) before Confirm enables. */\n confirmText?: string;\n /** The typed-confirmation label. Defaults to \"Type <confirmText> to confirm\". */\n confirmTextLabel?: ReactNode;\n /** Close after a successful `onConfirm`. Defaults to `true`. */\n closeOnSuccess?: boolean;\n /** Keeps Confirm disabled, e.g. while the body's options are invalid. */\n confirmDisabled?: boolean;\n}\n\n/**\n * A yes/no dialog for an action, built on {@link Dialog}: an optional typed\n * confirmation for destructive ones, an async `onConfirm` with a pending\n * state, and the failure shown in place so the user can retry or cancel.\n */\nexport function ConfirmDialog(props: ConfirmDialogProps) {\n return props.open ? <OpenConfirmDialog {...props} /> : null;\n}\n\n/** The mounted dialog; its state lives exactly as long as one opening. */\nfunction OpenConfirmDialog({\n onClose,\n title,\n description,\n children,\n onConfirm,\n confirmLabel = \"Confirm\",\n pendingLabel,\n cancelLabel = \"Cancel\",\n variant = \"default\",\n confirmText,\n confirmTextLabel,\n closeOnSuccess = true,\n confirmDisabled = false,\n}: ConfirmDialogProps) {\n const [typed, setTyped] = useState(\"\");\n const [pending, setPending] = useState(false);\n const [error, setError] = useState<unknown>(null);\n const cancelRef = useRef<HTMLButtonElement>(null);\n const confirmRef = useRef<HTMLButtonElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n\n const matches = confirmText === undefined || typed === confirmText;\n const canConfirm = matches && !pending && !confirmDisabled;\n const danger = variant === \"danger\";\n\n const confirm = async () => {\n if (!canConfirm) {\n return;\n }\n setPending(true);\n setError(null);\n try {\n await onConfirm();\n setPending(false);\n if (closeOnSuccess) {\n onClose();\n }\n } catch (caught) {\n setPending(false);\n setError(caught);\n }\n };\n\n const onSubmit = (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n void confirm();\n };\n\n const close = () => {\n if (!pending) {\n onClose();\n }\n };\n\n return (\n <Dialog\n open\n onClose={close}\n title={title}\n description={description}\n role={danger ? \"alertdialog\" : \"dialog\"}\n size=\"sm\"\n closeOnEscape={!pending}\n closeOnBackdrop={!pending}\n initialFocusRef={\n confirmText !== undefined ? inputRef : danger ? cancelRef : confirmRef\n }\n footer={\n <>\n <Button\n ref={cancelRef}\n onClick={close}\n disabled={pending}\n >\n {cancelLabel}\n </Button>\n <Button\n ref={confirmRef}\n variant={danger ? \"danger\" : \"primary\"}\n disabled={!canConfirm}\n aria-busy={pending || undefined}\n onClick={() => void confirm()}\n >\n {pending ? (pendingLabel ?? `${confirmLabel}…`) : confirmLabel}\n </Button>\n </>\n }\n >\n <form\n className=\"confirm-dialog-body\"\n onSubmit={onSubmit}\n >\n {children}\n {confirmText !== undefined && (\n <Field\n label={\n confirmTextLabel ?? (\n <>\n Type <code>{confirmText}</code> to confirm\n </>\n )\n }\n >\n <TextInput\n ref={inputRef}\n value={typed}\n onChange={setTyped}\n autoComplete=\"off\"\n spellCheck={false}\n disabled={pending}\n />\n </Field>\n )}\n {error !== null && <ProblemBanner error={error} />}\n </form>\n </Dialog>\n );\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "qFAiDO,SAAS,CAAa,CAAC,EAA2B,CACvD,OAAO,EAAM,KAAO,EAAC,EAAD,IAAuB,EAAO,EAAK,KAIzD,SAAS,CAAiB,EACxB,UACA,QACA,cACA,WACA,YACA,eAAe,UACf,eACA,cAAc,SACd,UAAU,UACV,cACA,mBACA,iBAAiB,GACjB,kBAAkB,IACG,CACrB,IAAO,EAAO,GAAY,EAAS,EAAE,GAC9B,EAAS,GAAc,EAAS,EAAK,GACrC,EAAO,GAAY,EAAkB,IAAI,EAC1C,EAAY,EAA0B,IAAI,EAC1C,EAAa,EAA0B,IAAI,EAC3C,EAAW,EAAyB,IAAI,EAGxC,GADU,IAAgB,QAAa,IAAU,IACzB,CAAC,GAAW,CAAC,EACrC,EAAS,IAAY,SAErB,EAAU,SAAY,CAC1B,GAAI,CAAC,EACH,OAEF,EAAW,EAAI,EACf,EAAS,IAAI,EACb,GAAI,CAGF,GAFA,MAAM,EAAU,EAChB,EAAW,EAAK,EACZ,EACF,EAAQ,EAEV,MAAO,EAAQ,CACf,EAAW,EAAK,EAChB,EAAS,CAAM,IAIb,EAAW,CAAC,IAAsC,CACtD,EAAM,eAAe,EAChB,EAAQ,GAGT,EAAQ,IAAM,CAClB,GAAI,CAAC,EACH,EAAQ,GAIZ,OACE,EAAC,GAAD,CACE,KAAI,GACJ,QAAS,EACT,MAAO,EACP,YAAa,EACb,KAAM,EAAS,cAAgB,SAC/B,KAAK,KACL,cAAe,CAAC,EAChB,gBAAiB,CAAC,EAClB,gBACE,IAAgB,OAAY,EAAW,EAAS,EAAY,EAE9D,OACE,cAiBE,CAhBA,EAAC,EAAD,CACE,IAAK,EACL,QAAS,EACT,SAAU,EAHZ,SAKG,EACD,EACF,EAAC,EAAD,CACE,IAAK,EACL,QAAS,EAAS,SAAW,UAC7B,SAAU,CAAC,EACX,YAAW,GAAW,OACtB,QAAS,IAAM,KAAK,EAAQ,EAL9B,SAOG,EAAW,GAAgB,GAAG,KAAmB,EAClD,GACF,EA9BN,SAiCE,EAAC,OAAD,CACE,UAAU,sBACV,SAAU,EAFZ,SA0BE,CAtBC,EACA,IAAgB,QACf,EAAC,EAAD,CACE,MACE,GACE,cAEE,CAFF,QACO,EAAC,OAAD,UAAO,EAAc,EAD5B,eAEE,EALR,SASE,EAAC,EAAD,CACE,IAAK,EACL,MAAO,EACP,SAAU,EACV,aAAa,MACb,WAAY,GACZ,SAAU,EACZ,EACA,EAEH,IAAU,MAAQ,EAAC,EAAD,CAAe,MAAO,EAAO,GAChD,EACF",
|
|
8
|
+
"debugId": "01B35119F2CD3AC464756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{e,t,p,s,a,fe,l}from"./main-xf44bv26.js";import{On,A}from"./main-9qd3nqs5.js";import{ra,oa}from"./main-zx20z2e9.js";function v(n,d=""){return`/providers/${encodeURIComponent(n)}${d}`}var Kt={list:["providers"],schema:(n)=>["providers","schema",n]};async function En(n,d){let r=await n.request("GET","/providers",{signal:d});return fe(r,(o)=>Array.isArray(o.providers)&&o.providers.every((i)=>typeof i==="object"&&i!==null&&typeof i.id==="string"&&typeof i.readiness==="string"),"the providers","/providers")}async function u(n,d){let c=v(d,"/validate"),r=await n.request("POST",c,{body:{}});return fe(r,(o)=>typeof o.ok==="boolean"&&Array.isArray(o.checks),"a provider validation",c)}async function ea(n,d,c){let r=v(d,"/schema"),o=await n.request("GET",r,{signal:c});return fe(o,(i)=>typeof i.schema==="object"&&i.schema!==null,"a provider's config schema",r)}function Nn({providerId:n,label:d}){let c=s(),r=On({mutationFn:()=>u(c,n)}),o=r.data;return t("div",{className:"provider-test","data-testid":`provider-test-${n}`,children:[e(a,{size:"sm",onClick:()=>r.mutate(),disabled:r.isPending,"aria-label":`Test connection: ${d}`,children:r.isPending?"Testing…":"Test connection"}),r.isError&&e(A,{error:r.error,title:"Could not run the test"}),o!==void 0&&!r.isPending&&t("div",{className:"provider-test-result",role:"status","data-testid":"provider-test-result","data-ok":String(o.ok),children:[e("p",{className:o.ok?"provider-test-ok":"provider-test-fail",children:oa(o)}),o.checks.length>0&&e("ul",{className:"provider-checks",children:o.checks.map((i)=>{let m=ra(i.status);return t("li",{"data-testid":`provider-check-${i.id}`,children:[e(p,{tone:m.tone,children:m.label})," ",e("code",{children:l(i.id)}),i.detail!==void 0&&t("span",{className:"muted",children:[" ","— ",l(i.detail)]})]},i.id)})})]})]})}
|
|
2
|
+
export{Kt,En,ea,Nn};
|
|
3
|
+
|
|
4
|
+
//# debugId=FCAF8F2DC7DD966964756E2164756E21
|
|
5
|
+
//# sourceMappingURL=main-vargz49c.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["app/jobs/api/providers.ts", "app/jobs/screens/providers/TestConnection.tsx"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { ApiClient } from \"./client\";\nimport type {\n ProviderListDto,\n ProviderSchemaDto,\n ProviderValidationDto,\n} from \"./types\";\nimport { assertShape } from \"./shape\";\n\n/**\n * The compute providers configured in the API's process: `GET /providers`\n * (`providers.read`), \"Test connection\" (`POST /providers/:id/validate`,\n * `providers.validate`) and a provider's config schema (`GET\n * /providers/:id/schema`, `providers.read`).\n *\n * Both actions are opt-in, and a disabled one is not routed at all (404\n * `ROUTE_NOT_FOUND`), the way `queues.summon` is. So the UI gates on the\n * permission first and reads nothing without it. A provider id is\n * `name@version~n`, which needs percent-encoding in a path.\n */\n\n/** `/providers/:id…`, relative to the API base. */\nfunction providerPath(id: string, suffix = \"\"): string {\n return `/providers/${encodeURIComponent(id)}${suffix}`;\n}\n\n/** Query keys of the provider reads. */\nexport const providerKeys = {\n /** `GET /providers`. */\n list: [\"providers\"] as const,\n /** `GET /providers/:id/schema`. */\n schema: (id: string) => [\"providers\", \"schema\", id] as const,\n};\n\n/** `GET /providers`. */\nexport async function listProviders(\n api: ApiClient,\n signal?: AbortSignal,\n): Promise<ProviderListDto> {\n const path = \"/providers\";\n const body = await api.request<unknown>(\"GET\", path, { signal });\n return assertShape<ProviderListDto>(\n body,\n (fields) =>\n Array.isArray(fields.providers) &&\n fields.providers.every(\n (item: unknown) =>\n typeof item === \"object\" &&\n item !== null &&\n typeof (item as { id?: unknown }).id === \"string\" &&\n typeof (item as { readiness?: unknown }).readiness === \"string\",\n ),\n \"the providers\",\n path,\n );\n}\n\n/**\n * `POST /providers/:id/validate`: the provider's preflight (\"Test\n * connection\"). A preflight that fails still answers 200, with `ok: false`\n * and the checks or an `error`, so only a refused or broken request throws.\n */\nexport async function validateProvider(\n api: ApiClient,\n id: string,\n): Promise<ProviderValidationDto> {\n const path = providerPath(id, \"/validate\");\n const body = await api.request<unknown>(\"POST\", path, { body: {} });\n return assertShape<ProviderValidationDto>(\n body,\n (fields) => typeof fields.ok === \"boolean\" && Array.isArray(fields.checks),\n \"a provider validation\",\n path,\n );\n}\n\n/** `GET /providers/:id/schema`: the provider's config schema, secret-free. */\nexport async function getProviderSchema(\n api: ApiClient,\n id: string,\n signal?: AbortSignal,\n): Promise<ProviderSchemaDto> {\n const path = providerPath(id, \"/schema\");\n const body = await api.request<unknown>(\"GET\", path, { signal });\n return assertShape<ProviderSchemaDto>(\n body,\n (fields) => typeof fields.schema === \"object\" && fields.schema !== null,\n \"a provider's config schema\",\n path,\n );\n}\n",
|
|
6
|
+
"import { useMutation } from \"@tanstack/react-query\";\nimport { validateProvider } from \"../../api/providers\";\nimport { Badge } from \"../../components/Badge\";\nimport { Button } from \"../../components/Button\";\nimport { ProblemBanner } from \"../../components/ProblemBanner\";\nimport { useApiClient } from \"../../context\";\nimport { displayText } from \"../../format\";\nimport { providerCheck, validationSummary } from \"./providerText\";\n\n/** Props of {@link TestConnection}. */\nexport interface TestConnectionProps {\n /** The provider's id, `name@version~n`. */\n providerId: string;\n /** What the button's accessible name says it tests, e.g. the provider's name. */\n label: string;\n}\n\n/**\n * \"Test connection\": runs the provider's preflight (`POST\n * /providers/:id/validate`) and shows what it found in place, the summary\n * first and then each check. The caller renders it only where the provider\n * has a preflight and the caller may validate (`providers.validate`, never\n * read-only).\n *\n * A failed preflight is an answer, not an error: it says whose problem it\n * is (the config's, the credentials', the platform's) or that it timed out.\n * Only a refused or broken request shows as a problem banner.\n */\nexport function TestConnection({ providerId, label }: TestConnectionProps) {\n const api = useApiClient();\n const test = useMutation({\n mutationFn: () => validateProvider(api, providerId),\n });\n const result = test.data;\n return (\n <div\n className=\"provider-test\"\n data-testid={`provider-test-${providerId}`}\n >\n <Button\n size=\"sm\"\n onClick={() => test.mutate()}\n disabled={test.isPending}\n aria-label={`Test connection: ${label}`}\n >\n {test.isPending ? \"Testing…\" : \"Test connection\"}\n </Button>\n {test.isError && (\n <ProblemBanner\n error={test.error}\n title=\"Could not run the test\"\n />\n )}\n {result !== undefined && !test.isPending && (\n <div\n className=\"provider-test-result\"\n role=\"status\"\n data-testid=\"provider-test-result\"\n data-ok={String(result.ok)}\n >\n <p className={result.ok ? \"provider-test-ok\" : \"provider-test-fail\"}>\n {validationSummary(result)}\n </p>\n {result.checks.length > 0 && (\n <ul className=\"provider-checks\">\n {result.checks.map((check) => {\n const status = providerCheck(check.status);\n return (\n <li\n key={check.id}\n data-testid={`provider-check-${check.id}`}\n >\n <Badge tone={status.tone}>{status.label}</Badge>{\" \"}\n <code>{displayText(check.id)}</code>\n {check.detail !== undefined && (\n <span className=\"muted\">\n {\" \"}\n — {displayText(check.detail)}\n </span>\n )}\n </li>\n );\n })}\n </ul>\n )}\n </div>\n )}\n </div>\n );\n}\n"
|
|
7
|
+
],
|
|
8
|
+
"mappings": "0HAqBA,SAAS,CAAY,CAAC,EAAY,EAAS,GAAY,CACrD,MAAO,cAAc,mBAAmB,CAAE,IAAI,IAIzC,IAAM,GAAe,CAE1B,KAAM,CAAC,WAAW,EAElB,OAAQ,CAAC,IAAe,CAAC,YAAa,SAAU,CAAE,CACpD,EAGO,eAAe,EAAa,CACjC,EACA,EAC0B,CAE1B,IAAM,EAAO,MAAM,EAAI,QAAiB,MAD3B,aACwC,CAAE,QAAO,CAAC,EAC/D,OAAO,GACL,EACA,CAAC,IACC,MAAM,QAAQ,EAAO,SAAS,GAC9B,EAAO,UAAU,MACf,CAAC,IACC,OAAO,IAAS,UAChB,IAAS,MACT,OAAQ,EAA0B,KAAO,UACzC,OAAQ,EAAiC,YAAc,QAC3D,EACF,gBAbW,YAeb,EAQK,eAAe,CAAgB,CACpC,EACA,EACgC,CAChC,IAAM,EAAO,EAAa,EAAI,WAAW,EACnC,EAAO,MAAM,EAAI,QAAiB,OAAQ,EAAM,CAAE,KAAM,CAAC,CAAE,CAAC,EAClE,OAAO,GACL,EACA,CAAC,IAAW,OAAO,EAAO,KAAO,WAAa,MAAM,QAAQ,EAAO,MAAM,EACzE,wBACA,CACF,EAIK,eAAe,EAAiB,CACrC,EACA,EACA,EAC4B,CAC5B,IAAM,EAAO,EAAa,EAAI,SAAS,EACjC,EAAO,MAAM,EAAI,QAAiB,MAAO,EAAM,CAAE,QAAO,CAAC,EAC/D,OAAO,GACL,EACA,CAAC,IAAW,OAAO,EAAO,SAAW,UAAY,EAAO,SAAW,KACnE,6BACA,CACF,EC5DK,SAAS,EAAc,EAAG,aAAY,SAA8B,CACzE,IAAM,EAAM,EAAa,EACnB,EAAO,GAAY,CACvB,WAAY,IAAM,EAAiB,EAAK,CAAU,CACpD,CAAC,EACK,EAAS,EAAK,KACpB,OACE,EAAC,MAAD,CACE,UAAU,gBACV,cAAa,iBAAiB,IAFhC,SAoDE,CAhDA,EAAC,EAAD,CACE,KAAK,KACL,QAAS,IAAM,EAAK,OAAO,EAC3B,SAAU,EAAK,UACf,aAAY,oBAAoB,IAJlC,SAMG,EAAK,UAAY,WAAa,kBAC/B,EACD,EAAK,SACJ,EAAC,EAAD,CACE,MAAO,EAAK,MACZ,MAAM,yBACR,EAED,IAAW,QAAa,CAAC,EAAK,WAC7B,EAAC,MAAD,CACE,UAAU,uBACV,KAAK,SACL,cAAY,uBACZ,UAAS,OAAO,EAAO,EAAE,EAJ3B,SA+BE,CAzBA,EAAC,IAAD,CAAG,UAAW,EAAO,GAAK,mBAAqB,qBAA/C,SACG,GAAkB,CAAM,EACzB,EACD,EAAO,OAAO,OAAS,GACtB,EAAC,KAAD,CAAI,UAAU,kBAAd,SACG,EAAO,OAAO,IAAI,CAAC,IAAU,CAC5B,IAAM,EAAS,GAAc,EAAM,MAAM,EACzC,OACE,EAAC,KAAD,CAEE,cAAa,kBAAkB,EAAM,KAFvC,SAYE,CARA,EAAC,EAAD,CAAO,KAAM,EAAO,KAApB,SAA2B,EAAO,MAAQ,EAAO,IACjD,EAAC,OAAD,UAAO,EAAY,EAAM,EAAE,EAAI,EAC9B,EAAM,SAAW,QAChB,EAAC,OAAD,CAAM,UAAU,QAAhB,SAGE,CAFC,IADH,KAEK,EAAY,EAAM,MAAM,GAC3B,IATC,EAAM,EAWX,EAEL,EACD,GAEJ,GAEJ",
|
|
9
|
+
"debugId": "FCAF8F2DC7DD966964756E2164756E21",
|
|
10
|
+
"names": []
|
|
11
|
+
}
|