@hexclave/shared 1.0.49 → 1.0.52
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/dist/{admin-interface-BPBTfhZC.d.ts → admin-interface-D9wMduWc.d.ts} +3 -3
- package/dist/{admin-interface-BPBTfhZC.d.ts.map → admin-interface-D9wMduWc.d.ts.map} +1 -1
- package/dist/ai/unified-prompts/reminders.js +2 -0
- package/dist/ai/unified-prompts/reminders.js.map +1 -1
- package/dist/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.d.ts.map +1 -1
- package/dist/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js +14 -29
- package/dist/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js.map +1 -1
- package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.d.ts +5 -4
- package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js +5 -2
- package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js.map +1 -1
- package/dist/apps/apps-config.d.ts +6 -0
- package/dist/apps/apps-config.d.ts.map +1 -1
- package/dist/apps/apps-config.js +6 -0
- package/dist/apps/apps-config.js.map +1 -1
- package/dist/config/schema.d.ts +1 -1
- package/dist/config-authoring.d.ts +1 -1
- package/dist/esm/ai/unified-prompts/reminders.js +2 -0
- package/dist/esm/ai/unified-prompts/reminders.js.map +1 -1
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.d.ts.map +1 -1
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js +14 -29
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js.map +1 -1
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.d.ts +5 -4
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js +5 -2
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js.map +1 -1
- package/dist/esm/apps/apps-config.d.ts +6 -0
- package/dist/esm/apps/apps-config.d.ts.map +1 -1
- package/dist/esm/apps/apps-config.js +6 -0
- package/dist/esm/apps/apps-config.js.map +1 -1
- package/dist/esm/config/schema.d.ts +3 -3
- package/dist/esm/interface/conversations.d.ts +29 -29
- package/dist/esm/interface/plan-usage.d.ts +1 -1
- package/dist/esm/utils/esbuild.d.ts.map +1 -1
- package/dist/esm/utils/esbuild.js +8 -3
- package/dist/esm/utils/esbuild.js.map +1 -1
- package/dist/esm/utils/promises.d.ts +3 -1
- package/dist/esm/utils/promises.d.ts.map +1 -1
- package/dist/esm/utils/promises.js +5 -2
- package/dist/esm/utils/promises.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/interface/admin-interface.d.ts +2 -2
- package/dist/interface/conversations.d.ts +29 -29
- package/dist/interface/plan-usage.d.ts +1 -1
- package/dist/{plan-usage-BO2GsKlN.d.ts → plan-usage-BaDxcLZ1.d.ts} +2 -2
- package/dist/{plan-usage-BO2GsKlN.d.ts.map → plan-usage-BaDxcLZ1.d.ts.map} +1 -1
- package/dist/{schema-Daq0b_sf.d.ts → schema-DV0SQUbY.d.ts} +4 -4
- package/dist/{schema-Daq0b_sf.d.ts.map → schema-DV0SQUbY.d.ts.map} +1 -1
- package/dist/utils/esbuild.d.ts.map +1 -1
- package/dist/utils/esbuild.js +8 -3
- package/dist/utils/esbuild.js.map +1 -1
- package/dist/utils/promises.d.ts +3 -1
- package/dist/utils/promises.d.ts.map +1 -1
- package/dist/utils/promises.js +5 -2
- package/dist/utils/promises.js.map +1 -1
- package/package.json +1 -1
- package/src/ai/unified-prompts/reminders.ts +2 -0
- package/src/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.ts +44 -29
- package/src/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.ts +7 -4
- package/src/apps/apps-config.ts +6 -0
- package/src/utils/esbuild.tsx +18 -3
- package/src/utils/promises.tsx +9 -2
|
@@ -197,7 +197,7 @@ declare const branchConfigSchema: yup$1.Schema<Omit<Omit<{
|
|
|
197
197
|
};
|
|
198
198
|
};
|
|
199
199
|
apps: {
|
|
200
|
-
installed: Record<"authentication" | "fraud-protection" | "analytics" | "onboarding" | "teams" | "rbac" | "api-keys" | "payments" | "emails" | "support" | "email-api" | "data-vault" | "webhooks" | "tv-mode" | "launch-checklist" | "catalyst" | "neon" | "convex" | "vercel" | "tanstack-start" | "clickmaps" | "session-replays", {
|
|
200
|
+
installed: Record<"authentication" | "fraud-protection" | "analytics" | "onboarding" | "teams" | "rbac" | "api-keys" | "payments" | "emails" | "support" | "email-api" | "data-vault" | "webhooks" | "tv-mode" | "launch-checklist" | "catalyst" | "neon" | "convex" | "vercel" | "tanstack-start" | "clickmaps" | "session-replays" | "cli-auth", {
|
|
201
201
|
enabled?: boolean | undefined;
|
|
202
202
|
}>;
|
|
203
203
|
};
|
|
@@ -397,7 +397,7 @@ declare const environmentConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<Omit<{
|
|
|
397
397
|
};
|
|
398
398
|
};
|
|
399
399
|
apps: {
|
|
400
|
-
installed: Record<"authentication" | "fraud-protection" | "analytics" | "onboarding" | "teams" | "rbac" | "api-keys" | "payments" | "emails" | "support" | "email-api" | "data-vault" | "webhooks" | "tv-mode" | "launch-checklist" | "catalyst" | "neon" | "convex" | "vercel" | "tanstack-start" | "clickmaps" | "session-replays", {
|
|
400
|
+
installed: Record<"authentication" | "fraud-protection" | "analytics" | "onboarding" | "teams" | "rbac" | "api-keys" | "payments" | "emails" | "support" | "email-api" | "data-vault" | "webhooks" | "tv-mode" | "launch-checklist" | "catalyst" | "neon" | "convex" | "vercel" | "tanstack-start" | "clickmaps" | "session-replays" | "cli-auth", {
|
|
401
401
|
enabled?: boolean | undefined;
|
|
402
402
|
}>;
|
|
403
403
|
};
|
|
@@ -1130,7 +1130,7 @@ declare const organizationConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<NonNu
|
|
|
1130
1130
|
};
|
|
1131
1131
|
};
|
|
1132
1132
|
apps: {
|
|
1133
|
-
installed: Record<"authentication" | "fraud-protection" | "analytics" | "onboarding" | "teams" | "rbac" | "api-keys" | "payments" | "emails" | "support" | "email-api" | "data-vault" | "webhooks" | "tv-mode" | "launch-checklist" | "catalyst" | "neon" | "convex" | "vercel" | "tanstack-start" | "clickmaps" | "session-replays", {
|
|
1133
|
+
installed: Record<"authentication" | "fraud-protection" | "analytics" | "onboarding" | "teams" | "rbac" | "api-keys" | "payments" | "emails" | "support" | "email-api" | "data-vault" | "webhooks" | "tv-mode" | "launch-checklist" | "catalyst" | "neon" | "convex" | "vercel" | "tanstack-start" | "clickmaps" | "session-replays" | "cli-auth", {
|
|
1134
1134
|
enabled?: boolean | undefined;
|
|
1135
1135
|
}>;
|
|
1136
1136
|
};
|
|
@@ -2286,4 +2286,4 @@ type OrganizationRenderedConfig = Expand<Awaited<ReturnType<typeof sanitizeOrgan
|
|
|
2286
2286
|
type CompleteConfig = OrganizationRenderedConfig;
|
|
2287
2287
|
//#endregion
|
|
2288
2288
|
export { branchConfigSchema as A, sanitizeEnvironmentConfig as B, ValidatedToHaveNoConfigOverrideErrors as C, applyOrganizationDefaults as D, applyEnvironmentDefaults as E, getIncompleteConfigWarnings as F, sanitizeProjectConfig as H, migrateConfigOverride as I, organizationConfigSchema as L, configLevels as M, environmentConfigSchema as N, applyProjectDefaults as O, getConfigOverrideErrors as P, projectConfigSchema as R, ProjectRenderedConfig as S, applyBranchDefaults as T, sanitizeOrganizationConfig as V, OrganizationRenderedConfig as _, BranchRenderedConfig as a, ProjectConfigOverrideOverride as b, EnvironmentConfigNormalizedOverride as c, EnvironmentIncompleteConfig as d, EnvironmentRenderedConfig as f, OrganizationIncompleteConfig as g, OrganizationConfigOverrideOverride as h, BranchIncompleteConfig as i, branchPaymentsSchema as j, assertNoConfigOverrideErrors as k, EnvironmentConfigOverride as l, OrganizationConfigOverride as m, BranchConfigOverride as n, CompleteConfig as o, OrganizationConfigNormalizedOverride as p, BranchConfigOverrideOverride as r, ConfigLevel as s, BranchConfigNormalizedOverride as t, EnvironmentConfigOverrideOverride as u, ProjectConfigNormalizedOverride as v, ValidatedToHaveNoIncompleteConfigWarnings as w, ProjectIncompleteConfig as x, ProjectConfigOverride as y, sanitizeBranchConfig as z };
|
|
2289
|
-
//# sourceMappingURL=schema-
|
|
2289
|
+
//# sourceMappingURL=schema-DV0SQUbY.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-
|
|
1
|
+
{"version":3,"file":"schema-DV0SQUbY.d.ts","names":[],"sources":["../src/config/schema.ts"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"esbuild.d.ts","names":[],"sources":["../../src/utils/esbuild.tsx"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"esbuild.d.ts","names":[],"sources":["../../src/utils/esbuild.tsx"],"mappings":";;;iBAyCgB,iBAAA,IAAqB,OAAO;AAAA,iBAgDtB,gBAAA,CAAiB,WAAA,EAAa,MAAA;EAA2B,WAAA;AAAA,GAAuB,OAAA;EACpG,MAAA;EACA,gBAAA,GAAmB,MAAA;EACnB,aAAA;EACA,SAAA;EACA,gBAAA;AAAA,IACO,OAAA,CAAQ,MAAA"}
|
package/dist/utils/esbuild.js
CHANGED
|
@@ -10,6 +10,11 @@ let esbuild_wasm_lib_browser_js = require("esbuild-wasm/lib/browser.js");
|
|
|
10
10
|
esbuild_wasm_lib_browser_js = require_rolldown_runtime.__toESM(esbuild_wasm_lib_browser_js);
|
|
11
11
|
let __telemetry_js = require("./telemetry.js");
|
|
12
12
|
//#region src/utils/esbuild.tsx
|
|
13
|
+
const esbuildModule = esbuild_wasm_lib_browser_js;
|
|
14
|
+
function getEsbuildApi(module) {
|
|
15
|
+
return module.version == null && module.default != null ? module.default : esbuild_wasm_lib_browser_js;
|
|
16
|
+
}
|
|
17
|
+
const esbuildApi = getEsbuildApi(esbuildModule);
|
|
13
18
|
globalThis.self ??= globalThis;
|
|
14
19
|
let esbuildInitializePromise = null;
|
|
15
20
|
if (typeof process !== "undefined" && typeof process.exit === "function" && (0, __env_js.getProcessEnv)("NODE_ENV") === "development") (0, __promises_js.runAsynchronously)(async () => {
|
|
@@ -20,7 +25,7 @@ if (typeof process !== "undefined" && typeof process.exit === "function" && (0,
|
|
|
20
25
|
}
|
|
21
26
|
});
|
|
22
27
|
function initializeEsbuild() {
|
|
23
|
-
const esbuildWasmUrl = `https://unpkg.com/esbuild-wasm@${
|
|
28
|
+
const esbuildWasmUrl = `https://unpkg.com/esbuild-wasm@${esbuildApi.version}/esbuild.wasm`;
|
|
24
29
|
if (esbuildInitializePromise == null) {
|
|
25
30
|
esbuildInitializePromise = (0, __telemetry_js.withTraceSpan)("initializeEsbuild", async () => {
|
|
26
31
|
try {
|
|
@@ -38,7 +43,7 @@ function initializeEsbuild() {
|
|
|
38
43
|
worker: false
|
|
39
44
|
};
|
|
40
45
|
try {
|
|
41
|
-
await
|
|
46
|
+
await esbuildApi.initialize(initOptions);
|
|
42
47
|
} catch (e) {
|
|
43
48
|
if (e instanceof Error && e.message === "Cannot call \"initialize\" more than once") {} else throw e;
|
|
44
49
|
}
|
|
@@ -67,7 +72,7 @@ async function bundleJavaScript(sourceFiles, options = {}) {
|
|
|
67
72
|
]);
|
|
68
73
|
let result;
|
|
69
74
|
try {
|
|
70
|
-
result = await (0, __telemetry_js.traceSpan)("bundleJavaScript", async () => await
|
|
75
|
+
result = await (0, __telemetry_js.traceSpan)("bundleJavaScript", async () => await esbuildApi.build({
|
|
71
76
|
entryPoints: ["/entry.js"],
|
|
72
77
|
bundle: true,
|
|
73
78
|
write: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"esbuild.js","names":["esbuild","HexclaveAssertionError","Result"],"sources":["../../src/utils/esbuild.tsx"],"sourcesContent":["import * as esbuild from 'esbuild-wasm/lib/browser.js';\nimport { join } from 'path';\nimport { getProcessEnv, isBrowserLike } from './env';\nimport { captureError, HexclaveAssertionError, throwErr } from \"./errors\";\nimport { createGlobalAsync } from './globals';\nimport { ignoreUnhandledRejection, runAsynchronously } from './promises';\nimport { Result } from \"./results\";\nimport { traceSpan, withTraceSpan } from './telemetry';\n\n\n// esbuild requires self property to be set, and it is not set by default in nodejs\n(globalThis.self as any) ??= globalThis as any;\n\nlet esbuildInitializePromise: Promise<void> | null = null;\n\nif (typeof process !== \"undefined\" && typeof process.exit === \"function\" && getProcessEnv(\"NODE_ENV\") === 'development') {\n // On development Node.js servers, initialize ESBuild as soon as the module is imported so we have to wait less on the first request\n runAsynchronously(async () => {\n try {\n await initializeEsbuild();\n } catch (e) {\n captureError(\"initialize-esbuild-in-dev\", e);\n }\n });\n}\n\nexport function initializeEsbuild(): Promise<void> {\n const esbuildWasmUrl = `https://unpkg.com/esbuild-wasm@${esbuild.version}/esbuild.wasm`;\n if (esbuildInitializePromise == null) {\n esbuildInitializePromise = withTraceSpan('initializeEsbuild', async () => {\n try {\n let initOptions;\n if (isBrowserLike()) {\n initOptions = {\n wasmURL: esbuildWasmUrl,\n };\n } else {\n const esbuildWasmModule = await createGlobalAsync('esbuildWasmModule', async () => {\n const esbuildWasmResponse = await fetch(esbuildWasmUrl);\n if (!esbuildWasmResponse.ok) {\n throw new HexclaveAssertionError(`Failed to fetch esbuild.wasm: ${esbuildWasmResponse.status} ${esbuildWasmResponse.statusText}: ${await esbuildWasmResponse.text()}`);\n }\n const esbuildWasm = await esbuildWasmResponse.arrayBuffer();\n const esbuildWasmArray = new Uint8Array(esbuildWasm);\n if (esbuildWasmArray[0] !== 0x00 || esbuildWasmArray[1] !== 0x61 || esbuildWasmArray[2] !== 0x73 || esbuildWasmArray[3] !== 0x6d) {\n throw new HexclaveAssertionError(`Invalid esbuild.wasm file: ${new TextDecoder().decode(esbuildWasmArray)}`);\n }\n return new WebAssembly.Module(esbuildWasm);\n });\n initOptions = {\n wasmModule: esbuildWasmModule,\n worker: false,\n };\n }\n try {\n await esbuild.initialize(initOptions);\n } catch (e) {\n if (e instanceof Error && e.message === 'Cannot call \"initialize\" more than once') {\n // this happens especially in local development, just ignore\n } else {\n throw e;\n }\n }\n } catch (e) {\n esbuildInitializePromise = null;\n throw new HexclaveAssertionError(\"Failed to initialize ESBuild\", { cause: e });\n }\n })();\n ignoreUnhandledRejection(esbuildInitializePromise);\n }\n\n return esbuildInitializePromise;\n}\n\nexport async function bundleJavaScript(sourceFiles: Record<string, string> & { '/entry.js': string }, options: {\n format?: 'iife' | 'esm' | 'cjs',\n externalPackages?: Record<string, string>,\n keepAsImports?: string[],\n sourcemap?: false | 'inline',\n allowHttpImports?: boolean,\n} = {}): Promise<Result<string, string>> {\n await initializeEsbuild();\n\n const sourceFilesMap = new Map(Object.entries(sourceFiles));\n const externalPackagesMap = new Map(Object.entries(options.externalPackages ?? {}));\n const keepAsImports = options.keepAsImports ?? [];\n\n const httpImportCache = new Map<string, { contents: string, loader: esbuild.Loader, resolveDir: string }>();\n\n const extToLoader: Map<string, esbuild.Loader> = new Map([\n ['tsx', 'tsx'],\n ['ts', 'ts'],\n ['js', 'js'],\n ['jsx', 'jsx'],\n ['json', 'json'],\n ['css', 'css'],\n ]);\n let result;\n try {\n result = await traceSpan('bundleJavaScript', async () => await esbuild.build({\n entryPoints: ['/entry.js'],\n bundle: true,\n write: false,\n format: options.format ?? 'iife',\n platform: 'browser',\n target: 'es2015',\n jsx: 'automatic',\n sourcemap: options.sourcemap ?? 'inline',\n external: keepAsImports,\n plugins: [\n ...options.allowHttpImports ? [{\n name: \"esm-sh-only\",\n setup(build: esbuild.PluginBuild) {\n // Handle absolute URLs and relative imports from esm.sh modules.\n build.onResolve({ filter: /.*/ }, (args) => {\n // Only touch absolute http(s) specifiers or children of our own namespace\n const isHttp = args.path.startsWith(\"http://\") || args.path.startsWith(\"https://\");\n const fromEsmNs = args.namespace === \"esm-sh\";\n\n if (!isHttp && !fromEsmNs) return null; // Let other plugins handle bare/relative/local\n\n // Resolve relative URLs inside esm.sh-fetched modules\n const url = new URL(args.path, fromEsmNs ? args.importer : undefined);\n\n if (url.protocol !== \"https:\" || url.host !== \"esm.sh\") {\n throw new Error(`Blocked non-esm.sh URL import: ${url.href}`);\n }\n\n return { path: url.href, namespace: \"esm-sh\" };\n });\n\n build.onLoad({ filter: /.*/, namespace: \"esm-sh\" }, async (args) => {\n if (httpImportCache.has(args.path)) return httpImportCache.get(args.path)!;\n\n const res = await fetch(args.path, { redirect: \"follow\" });\n if (!res.ok) throw new Error(`Fetch ${res.status} ${res.statusText} for ${args.path}`);\n const finalUrl = new URL(res.url);\n // Defensive: follow shouldn’t leave esm.sh, but re-check.\n if (finalUrl.host !== \"esm.sh\") {\n throw new Error(`Redirect escaped esm.sh: ${finalUrl.href}`);\n }\n\n const ct = (res.headers.get(\"content-type\") || \"\").toLowerCase();\n let loader: esbuild.Loader =\n ct.includes(\"css\") ? \"css\" :\n ct.includes(\"json\") ? \"json\" :\n ct.includes(\"typescript\") ? \"ts\" :\n ct.includes(\"jsx\") ? \"jsx\" :\n ct.includes(\"tsx\") ? \"tsx\" :\n \"js\";\n\n // Fallback by extension (esm.sh sometimes omits CT)\n const p = finalUrl.pathname;\n if (p.endsWith(\".css\")) loader = \"css\";\n else if (p.endsWith(\".json\")) loader = \"json\";\n else if (p.endsWith(\".ts\")) loader = \"ts\";\n else if (p.endsWith(\".tsx\")) loader = \"tsx\";\n else if (p.endsWith(\".jsx\")) loader = \"jsx\";\n\n const contents = await res.text();\n const result = {\n contents,\n loader,\n // Ensures relative imports inside that module resolve against the file’s URL\n resolveDir: new URL(\".\", finalUrl.href).toString(),\n watchFiles: [finalUrl.href],\n };\n httpImportCache.set(args.path, result);\n return result;\n });\n },\n } as esbuild.Plugin] : [],\n {\n name: 'replace-packages-with-globals',\n setup(build) {\n build.onResolve({ filter: /.*/ }, args => {\n // Skip packages that should remain external (not be shimmed)\n if (keepAsImports.includes(args.path)) {\n return undefined;\n }\n if (externalPackagesMap.has(args.path)) {\n return { path: args.path, namespace: 'package-shim' };\n }\n return undefined;\n });\n\n build.onLoad({ filter: /.*/, namespace: 'package-shim' }, (args) => {\n const contents = externalPackagesMap.get(args.path);\n if (contents == null) throw new HexclaveAssertionError(`esbuild requested file ${args.path} that is not in the virtual file system`);\n\n return { contents, loader: 'ts' };\n });\n },\n },\n {\n name: 'virtual-fs',\n setup(build) {\n build.onResolve({ filter: /.*/ }, args => {\n const absolutePath = join(\"/\", args.path);\n if (sourceFilesMap.has(absolutePath)) {\n return { path: absolutePath, namespace: 'virtual' };\n }\n return undefined;\n });\n\n /* 2️⃣ Load the module from the map */\n build.onLoad({ filter: /.*/, namespace: 'virtual' }, args => {\n const contents = sourceFilesMap.get(args.path);\n if (contents == null) throw new HexclaveAssertionError(`esbuild requested file ${args.path} that is not in the virtual file system`);\n\n const ext = args.path.split('.').pop() ?? '';\n const loader = extToLoader.get(ext) ?? throwErr(`esbuild requested file ${args.path} with unknown extension ${ext}`);\n\n return { contents, loader };\n });\n },\n },\n ],\n }));\n } catch (e) {\n if (e instanceof Error && e.message.startsWith(\"Build failed with \")) {\n return Result.error(e.message);\n }\n throw e;\n }\n\n if (result.errors.length > 0) {\n return Result.error(result.errors.map(e => e.text).join('\\n'));\n }\n\n if (result.outputFiles.length > 0) {\n return Result.ok(result.outputFiles[0].text);\n }\n return throwErr(\"No output generated??\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"esbuild.js","names":["esbuild","HexclaveAssertionError","Result"],"sources":["../../src/utils/esbuild.tsx"],"sourcesContent":["import * as esbuild from 'esbuild-wasm/lib/browser.js';\nimport { join } from 'path';\nimport { getProcessEnv, isBrowserLike } from './env';\nimport { captureError, HexclaveAssertionError, throwErr } from \"./errors\";\nimport { createGlobalAsync } from './globals';\nimport { ignoreUnhandledRejection, runAsynchronously } from './promises';\nimport { Result } from \"./results\";\nimport { traceSpan, withTraceSpan } from './telemetry';\n\ntype EsbuildBrowserRuntimeModule = Partial<typeof esbuild> & {\n default?: typeof esbuild,\n};\n\n// The built ESM package can import esbuild-wasm's CJS browser entry as a\n// default-only namespace under Node. Normalize once so both tsx source runs and\n// built dist runs see the same API surface.\nconst esbuildModule: EsbuildBrowserRuntimeModule = esbuild;\nfunction getEsbuildApi(module: EsbuildBrowserRuntimeModule): typeof esbuild {\n return module.version == null && module.default != null\n ? module.default\n : esbuild;\n}\nconst esbuildApi = getEsbuildApi(esbuildModule);\n\n\n// esbuild requires self property to be set, and it is not set by default in nodejs\n(globalThis.self as any) ??= globalThis as any;\n\nlet esbuildInitializePromise: Promise<void> | null = null;\n\nif (typeof process !== \"undefined\" && typeof process.exit === \"function\" && getProcessEnv(\"NODE_ENV\") === 'development') {\n // On development Node.js servers, initialize ESBuild as soon as the module is imported so we have to wait less on the first request\n runAsynchronously(async () => {\n try {\n await initializeEsbuild();\n } catch (e) {\n captureError(\"initialize-esbuild-in-dev\", e);\n }\n });\n}\n\nexport function initializeEsbuild(): Promise<void> {\n const esbuildWasmUrl = `https://unpkg.com/esbuild-wasm@${esbuildApi.version}/esbuild.wasm`;\n if (esbuildInitializePromise == null) {\n esbuildInitializePromise = withTraceSpan('initializeEsbuild', async () => {\n try {\n let initOptions;\n if (isBrowserLike()) {\n initOptions = {\n wasmURL: esbuildWasmUrl,\n };\n } else {\n const esbuildWasmModule = await createGlobalAsync('esbuildWasmModule', async () => {\n const esbuildWasmResponse = await fetch(esbuildWasmUrl);\n if (!esbuildWasmResponse.ok) {\n throw new HexclaveAssertionError(`Failed to fetch esbuild.wasm: ${esbuildWasmResponse.status} ${esbuildWasmResponse.statusText}: ${await esbuildWasmResponse.text()}`);\n }\n const esbuildWasm = await esbuildWasmResponse.arrayBuffer();\n const esbuildWasmArray = new Uint8Array(esbuildWasm);\n if (esbuildWasmArray[0] !== 0x00 || esbuildWasmArray[1] !== 0x61 || esbuildWasmArray[2] !== 0x73 || esbuildWasmArray[3] !== 0x6d) {\n throw new HexclaveAssertionError(`Invalid esbuild.wasm file: ${new TextDecoder().decode(esbuildWasmArray)}`);\n }\n return new WebAssembly.Module(esbuildWasm);\n });\n initOptions = {\n wasmModule: esbuildWasmModule,\n worker: false,\n };\n }\n try {\n await esbuildApi.initialize(initOptions);\n } catch (e) {\n if (e instanceof Error && e.message === 'Cannot call \"initialize\" more than once') {\n // this happens especially in local development, just ignore\n } else {\n throw e;\n }\n }\n } catch (e) {\n esbuildInitializePromise = null;\n throw new HexclaveAssertionError(\"Failed to initialize ESBuild\", { cause: e });\n }\n })();\n ignoreUnhandledRejection(esbuildInitializePromise);\n }\n\n return esbuildInitializePromise;\n}\n\nexport async function bundleJavaScript(sourceFiles: Record<string, string> & { '/entry.js': string }, options: {\n format?: 'iife' | 'esm' | 'cjs',\n externalPackages?: Record<string, string>,\n keepAsImports?: string[],\n sourcemap?: false | 'inline',\n allowHttpImports?: boolean,\n} = {}): Promise<Result<string, string>> {\n await initializeEsbuild();\n\n const sourceFilesMap = new Map(Object.entries(sourceFiles));\n const externalPackagesMap = new Map(Object.entries(options.externalPackages ?? {}));\n const keepAsImports = options.keepAsImports ?? [];\n\n const httpImportCache = new Map<string, { contents: string, loader: esbuild.Loader, resolveDir: string }>();\n\n const extToLoader: Map<string, esbuild.Loader> = new Map([\n ['tsx', 'tsx'],\n ['ts', 'ts'],\n ['js', 'js'],\n ['jsx', 'jsx'],\n ['json', 'json'],\n ['css', 'css'],\n ]);\n let result;\n try {\n result = await traceSpan('bundleJavaScript', async () => await esbuildApi.build({\n entryPoints: ['/entry.js'],\n bundle: true,\n write: false,\n format: options.format ?? 'iife',\n platform: 'browser',\n target: 'es2015',\n jsx: 'automatic',\n sourcemap: options.sourcemap ?? 'inline',\n external: keepAsImports,\n plugins: [\n ...options.allowHttpImports ? [{\n name: \"esm-sh-only\",\n setup(build: esbuild.PluginBuild) {\n // Handle absolute URLs and relative imports from esm.sh modules.\n build.onResolve({ filter: /.*/ }, (args) => {\n // Only touch absolute http(s) specifiers or children of our own namespace\n const isHttp = args.path.startsWith(\"http://\") || args.path.startsWith(\"https://\");\n const fromEsmNs = args.namespace === \"esm-sh\";\n\n if (!isHttp && !fromEsmNs) return null; // Let other plugins handle bare/relative/local\n\n // Resolve relative URLs inside esm.sh-fetched modules\n const url = new URL(args.path, fromEsmNs ? args.importer : undefined);\n\n if (url.protocol !== \"https:\" || url.host !== \"esm.sh\") {\n throw new Error(`Blocked non-esm.sh URL import: ${url.href}`);\n }\n\n return { path: url.href, namespace: \"esm-sh\" };\n });\n\n build.onLoad({ filter: /.*/, namespace: \"esm-sh\" }, async (args) => {\n if (httpImportCache.has(args.path)) return httpImportCache.get(args.path)!;\n\n const res = await fetch(args.path, { redirect: \"follow\" });\n if (!res.ok) throw new Error(`Fetch ${res.status} ${res.statusText} for ${args.path}`);\n const finalUrl = new URL(res.url);\n // Defensive: follow shouldn’t leave esm.sh, but re-check.\n if (finalUrl.host !== \"esm.sh\") {\n throw new Error(`Redirect escaped esm.sh: ${finalUrl.href}`);\n }\n\n const ct = (res.headers.get(\"content-type\") || \"\").toLowerCase();\n let loader: esbuild.Loader =\n ct.includes(\"css\") ? \"css\" :\n ct.includes(\"json\") ? \"json\" :\n ct.includes(\"typescript\") ? \"ts\" :\n ct.includes(\"jsx\") ? \"jsx\" :\n ct.includes(\"tsx\") ? \"tsx\" :\n \"js\";\n\n // Fallback by extension (esm.sh sometimes omits CT)\n const p = finalUrl.pathname;\n if (p.endsWith(\".css\")) loader = \"css\";\n else if (p.endsWith(\".json\")) loader = \"json\";\n else if (p.endsWith(\".ts\")) loader = \"ts\";\n else if (p.endsWith(\".tsx\")) loader = \"tsx\";\n else if (p.endsWith(\".jsx\")) loader = \"jsx\";\n\n const contents = await res.text();\n const result = {\n contents,\n loader,\n // Ensures relative imports inside that module resolve against the file’s URL\n resolveDir: new URL(\".\", finalUrl.href).toString(),\n watchFiles: [finalUrl.href],\n };\n httpImportCache.set(args.path, result);\n return result;\n });\n },\n } as esbuild.Plugin] : [],\n {\n name: 'replace-packages-with-globals',\n setup(build) {\n build.onResolve({ filter: /.*/ }, args => {\n // Skip packages that should remain external (not be shimmed)\n if (keepAsImports.includes(args.path)) {\n return undefined;\n }\n if (externalPackagesMap.has(args.path)) {\n return { path: args.path, namespace: 'package-shim' };\n }\n return undefined;\n });\n\n build.onLoad({ filter: /.*/, namespace: 'package-shim' }, (args) => {\n const contents = externalPackagesMap.get(args.path);\n if (contents == null) throw new HexclaveAssertionError(`esbuild requested file ${args.path} that is not in the virtual file system`);\n\n return { contents, loader: 'ts' };\n });\n },\n },\n {\n name: 'virtual-fs',\n setup(build) {\n build.onResolve({ filter: /.*/ }, args => {\n const absolutePath = join(\"/\", args.path);\n if (sourceFilesMap.has(absolutePath)) {\n return { path: absolutePath, namespace: 'virtual' };\n }\n return undefined;\n });\n\n /* 2️⃣ Load the module from the map */\n build.onLoad({ filter: /.*/, namespace: 'virtual' }, args => {\n const contents = sourceFilesMap.get(args.path);\n if (contents == null) throw new HexclaveAssertionError(`esbuild requested file ${args.path} that is not in the virtual file system`);\n\n const ext = args.path.split('.').pop() ?? '';\n const loader = extToLoader.get(ext) ?? throwErr(`esbuild requested file ${args.path} with unknown extension ${ext}`);\n\n return { contents, loader };\n });\n },\n },\n ],\n }));\n } catch (e) {\n if (e instanceof Error && e.message.startsWith(\"Build failed with \")) {\n return Result.error(e.message);\n }\n throw e;\n }\n\n if (result.errors.length > 0) {\n return Result.error(result.errors.map(e => e.text).join('\\n'));\n }\n\n if (result.outputFiles.length > 0) {\n return Result.ok(result.outputFiles[0].text);\n }\n return throwErr(\"No output generated??\");\n}\n"],"mappings":"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"}
|
package/dist/utils/promises.d.ts
CHANGED
|
@@ -38,7 +38,9 @@ declare function ignoreUnhandledRejection<T extends Promise<any>>(promise: T): v
|
|
|
38
38
|
* See concatStacktraces for more information.
|
|
39
39
|
*/
|
|
40
40
|
declare function concatStacktracesIfRejected<T>(promise: Promise<T>): void;
|
|
41
|
-
declare function wait(ms: number
|
|
41
|
+
declare function wait(ms: number, options?: {
|
|
42
|
+
unref?: boolean;
|
|
43
|
+
}): Promise<void>;
|
|
42
44
|
declare function waitUntil(date: Date): Promise<void>;
|
|
43
45
|
declare function runAsynchronouslyWithAlert(...args: Parameters<typeof runAsynchronously>): void;
|
|
44
46
|
declare function runAsynchronously(promiseOrFunc: void | Promise<unknown> | (() => void | Promise<unknown>) | undefined, options?: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promises.d.ts","names":[],"sources":["../../src/utils/promises.tsx"],"mappings":";;;KAQY,YAAA,MAAkB,OAAA,CAAQ,CAAA;EAChC,MAAA;EAAoB,MAAA;AAAA;EACpB,MAAA;EAAqB,KAAA,EAAO,CAAA;AAAA;EAC5B,MAAA;AAAA;AAAA,KAGD,OAAA,OAAc,KAAA,EAAO,CAAC;AAAA,KACtB,MAAA,IAAU,MAAe;AAAA,iBACd,aAAA,IAAiB,QAAA,GAAW,OAAA,EAAS,OAAA,CAAQ,CAAA,GAAI,MAAA,EAAQ,MAAA,YAAkB,YAAA,CAAa,CAAA;;;;;iBAwExF,QAAA,IAAY,KAAA,EAAO,CAAA,GAAI,YAAA,CAAa,CAAA;;;;AA7ExC;iBAuHI,QAAA,IAAY,MAAA,YAAkB,YAAY,CAAC,CAAA;AAAA,iBA6C3C,YAAA,IAAgB,YAAY;AAAA,iBAc5B,OAAA,IAAW,OAAA,EAAS,OAAA,CAAQ,CAAA,GAAI,OAAA;EAAW,oBAAA;AAAA,IAAwC,YAAA,CAAa,CAAA;;;;AA/KrF;AAAA;;iBAuNX,wBAAA,WAAmC,OAAA,OAAc,OAAA,EAAS,CAAC;;AAtN7C;AAC9B;iBA0OgB,2BAAA,IAA+B,OAAA,EAAS,OAAO,CAAC,CAAA;AAAA,iBAW1C,IAAA,CAAK,EAAA,
|
|
1
|
+
{"version":3,"file":"promises.d.ts","names":[],"sources":["../../src/utils/promises.tsx"],"mappings":";;;KAQY,YAAA,MAAkB,OAAA,CAAQ,CAAA;EAChC,MAAA;EAAoB,MAAA;AAAA;EACpB,MAAA;EAAqB,KAAA,EAAO,CAAA;AAAA;EAC5B,MAAA;AAAA;AAAA,KAGD,OAAA,OAAc,KAAA,EAAO,CAAC;AAAA,KACtB,MAAA,IAAU,MAAe;AAAA,iBACd,aAAA,IAAiB,QAAA,GAAW,OAAA,EAAS,OAAA,CAAQ,CAAA,GAAI,MAAA,EAAQ,MAAA,YAAkB,YAAA,CAAa,CAAA;;;;;iBAwExF,QAAA,IAAY,KAAA,EAAO,CAAA,GAAI,YAAA,CAAa,CAAA;;;;AA7ExC;iBAuHI,QAAA,IAAY,MAAA,YAAkB,YAAY,CAAC,CAAA;AAAA,iBA6C3C,YAAA,IAAgB,YAAY;AAAA,iBAc5B,OAAA,IAAW,OAAA,EAAS,OAAA,CAAQ,CAAA,GAAI,OAAA;EAAW,oBAAA;AAAA,IAAwC,YAAA,CAAa,CAAA;;;;AA/KrF;AAAA;;iBAuNX,wBAAA,WAAmC,OAAA,OAAc,OAAA,EAAS,CAAC;;AAtN7C;AAC9B;iBA0OgB,2BAAA,IAA+B,OAAA,EAAS,OAAO,CAAC,CAAA;AAAA,iBAW1C,IAAA,CAAK,EAAA,UAAY,OAAA;EAAY,KAAA;AAAA,IAAiB,OAAA;AAAA,iBAuC9C,SAAA,CAAU,IAAA,EAAM,IAAA,GAAI,OAAA;AAAA,iBAsB1B,0BAAA,IAA8B,IAAA,EAAM,UAAU,QAAQ,iBAAA;AAAA,iBAgCtD,iBAAA,CACd,aAAA,SAAsB,OAAA,0BAAiC,OAAA,wBACvD,OAAA;EACE,cAAA;EACA,OAAA,IAAW,KAAA,EAAO,KAAA;AAAA;AAAA,cAoChB,YAAA,SAAqB,KAAK;EAAA,SACF,EAAA;cAAA,EAAA;AAAA;AAAA,iBAMR,OAAA,IAAW,aAAA,EAAe,OAAA,CAAQ,CAAA,WAAY,OAAA,CAAQ,CAAA,IAAK,EAAA,WAAa,OAAA,CAAQ,MAAA,CAAO,CAAA,EAAG,YAAA;AAAA,iBA0B1F,YAAA,IAAgB,OAAA,EAAS,OAAA,CAAQ,CAAA,GAAI,EAAA,WAAa,OAAA,CAAQ,CAAA;;;;AA3ZyB;AAwEzG;;;;;;iBA6WsB,kBAAA,OACpB,KAAA,WAAgB,CAAA,IAChB,WAAA,UACA,EAAA,GAAK,IAAA,EAAM,CAAA,EAAG,KAAA,aAAkB,OAAA,CAAQ,CAAA,IACvC,OAAA,CAAQ,CAAA;AAAA,KAmDC,gBAAA;EApaa;;;EAwavB,WAAA;EAxakD;;AAAC;EA6anD,UAAA;EAnYsB;;;EAwYtB,UAAA;EAxY0B;;;EA6Y1B,KAAA;EA7Y0D;AA6C5D;;EAqWE,UAAA;AAAA;AAAA,iBAGc,WAAA,IACd,IAAA,QAAY,OAAA,CAAQ,CAAA,GACpB,OAAA,EAAS,gBAAA,SACF,OAAA,CAAQ,CAAA;AAAA,iBA0DD,SAAA,qBAA8B,IAAA,MAAU,IAAA,EAAM,CAAA,KAAM,OAAA,CAAQ,CAAA,GAAI,OAAA,eAAsB,IAAA,EAAM,CAAA,KAAM,OAAA,CAAQ,CAAA"}
|
package/dist/utils/promises.js
CHANGED
|
@@ -100,14 +100,17 @@ function concatStacktracesIfRejected(promise) {
|
|
|
100
100
|
if (error instanceof Error) (0, __errors_js.concatStacktraces)(error, currentError);
|
|
101
101
|
});
|
|
102
102
|
}
|
|
103
|
-
async function wait(ms) {
|
|
103
|
+
async function wait(ms, options) {
|
|
104
104
|
if (!Number.isFinite(ms) || ms < 0) throw new __errors_js.HexclaveAssertionError(`wait() requires a non-negative integer number of milliseconds to wait. (found: ${ms}ms)`);
|
|
105
105
|
if (ms >= 2 ** 31) throw new __errors_js.HexclaveAssertionError("The maximum timeout for wait() is 2147483647ms (2**31 - 1). (found: ${ms}ms)");
|
|
106
106
|
return await (0, __telemetry_js.traceSpan)({
|
|
107
107
|
description: "wait(...)",
|
|
108
108
|
attributes: { "stack.wait.ms": ms }
|
|
109
109
|
}, async (span) => {
|
|
110
|
-
return await new Promise((resolve) =>
|
|
110
|
+
return await new Promise((resolve) => {
|
|
111
|
+
const timeout = setTimeout(resolve, ms);
|
|
112
|
+
if (options?.unref === true && typeof timeout === "object") timeout.unref();
|
|
113
|
+
});
|
|
111
114
|
});
|
|
112
115
|
}
|
|
113
116
|
async function waitUntil(date) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promises.js","names":["DependenciesMap","HexclaveAssertionError","KnownError","Result"],"sources":["../../src/utils/promises.tsx"],"sourcesContent":["import { KnownError } from \"..\";\nimport { getProcessEnv } from \"./env\";\nimport { HexclaveAssertionError, captureError, concatStacktraces, errorToNiceString } from \"./errors\";\nimport { DependenciesMap } from \"./maps\";\nimport { Result } from \"./results\";\nimport { traceSpan } from \"./telemetry\";\nimport { generateUuid } from \"./uuids\";\n\nexport type ReactPromise<T> = Promise<T> & (\n | { status: \"rejected\", reason: unknown }\n | { status: \"fulfilled\", value: T }\n | { status: \"pending\" }\n);\n\ntype Resolve<T> = (value: T) => void;\ntype Reject = (reason: unknown) => void;\nexport function createPromise<T>(callback: (resolve: Resolve<T>, reject: Reject) => void): ReactPromise<T> {\n let status = \"pending\" as \"fulfilled\" | \"rejected\" | \"pending\";\n let valueOrReason: T | unknown | undefined = undefined;\n let resolve: Resolve<T> | null = null;\n let reject: Reject | null = null;\n const promise = new Promise<T>((res, rej) => {\n resolve = (value) => {\n if (status !== \"pending\") return;\n status = \"fulfilled\";\n valueOrReason = value;\n res(value);\n };\n reject = (reason) => {\n if (status !== \"pending\") return;\n status = \"rejected\";\n valueOrReason = reason;\n rej(reason);\n };\n });\n\n callback(resolve!, reject!);\n return Object.assign(promise, {\n status: status,\n ...status === \"fulfilled\" ? { value: valueOrReason as T } : {},\n ...status === \"rejected\" ? { reason: valueOrReason } : {},\n } as any);\n}\nimport.meta.vitest?.test(\"createPromise\", async ({ expect }) => {\n // Test resolved promise\n const resolvedPromise = createPromise<number>((resolve) => {\n resolve(42);\n });\n expect(resolvedPromise.status).toBe(\"fulfilled\");\n expect((resolvedPromise as any).value).toBe(42);\n expect(await resolvedPromise).toBe(42);\n\n // Test rejected promise\n const error = new Error(\"Test error\");\n const rejectedPromise = createPromise<number>((_, reject) => {\n reject(error);\n });\n expect(rejectedPromise.status).toBe(\"rejected\");\n expect((rejectedPromise as any).reason).toBe(error);\n await expect(rejectedPromise).rejects.toBe(error);\n\n // Test pending promise\n const pendingPromise = createPromise<number>(() => {\n // Do nothing, leave it pending\n });\n expect(pendingPromise.status).toBe(\"pending\");\n expect((pendingPromise as any).value).toBeUndefined();\n expect((pendingPromise as any).reason).toBeUndefined();\n\n // Test that resolving after already resolved does nothing\n let resolveCount = 0;\n const multiResolvePromise = createPromise<number>((resolve) => {\n resolve(1);\n resolveCount++;\n resolve(2);\n resolveCount++;\n });\n expect(resolveCount).toBe(2); // Both resolve calls executed\n expect(multiResolvePromise.status).toBe(\"fulfilled\");\n expect((multiResolvePromise as any).value).toBe(1); // Only first resolve took effect\n expect(await multiResolvePromise).toBe(1);\n});\n\nlet resolvedCache: DependenciesMap<[unknown], ReactPromise<unknown>> | null = null;\n/**\n * Like Promise.resolve(...), but also adds the status and value properties for use with React's `use` hook, and caches\n * the value so that invoking `resolved` twice returns the same promise.\n */\nexport function resolved<T>(value: T): ReactPromise<T> {\n resolvedCache ??= new DependenciesMap<[unknown], ReactPromise<unknown>>();\n if (resolvedCache.has([value])) {\n return resolvedCache.get([value]) as ReactPromise<T>;\n }\n\n const res = Object.assign(Promise.resolve(value), {\n status: \"fulfilled\",\n value,\n } as const);\n resolvedCache.set([value], res);\n return res;\n}\nimport.meta.vitest?.test(\"resolved\", async ({ expect }) => {\n // Test with primitive value\n const promise1 = resolved(42);\n expect(promise1.status).toBe(\"fulfilled\");\n // Need to use type assertion since value is only available when status is \"fulfilled\"\n expect((promise1 as { value: number }).value).toBe(42);\n expect(await promise1).toBe(42);\n\n // Test with object value\n const obj = { test: true };\n const promise2 = resolved(obj);\n expect(promise2.status).toBe(\"fulfilled\");\n expect((promise2 as { value: typeof obj }).value).toBe(obj);\n expect(await promise2).toBe(obj);\n\n // Test caching (same reference for same value)\n const promise3 = resolved(42);\n expect(promise3).toBe(promise1); // Same reference due to caching\n\n // Test with different value (different reference)\n const promise4 = resolved(43);\n expect(promise4).not.toBe(promise1);\n});\n\nlet rejectedCache: DependenciesMap<[unknown], ReactPromise<unknown>> | null = null;\n/**\n * Like Promise.reject(...), but also adds the status and value properties for use with React's `use` hook, and caches\n * the value so that invoking `rejected` twice returns the same promise.\n */\nexport function rejected<T>(reason: unknown): ReactPromise<T> {\n rejectedCache ??= new DependenciesMap<[unknown], ReactPromise<unknown>>();\n if (rejectedCache.has([reason])) {\n return rejectedCache.get([reason]) as ReactPromise<T>;\n }\n\n const promise = Promise.reject(reason);\n ignoreUnhandledRejection(promise);\n const res = Object.assign(promise, {\n status: \"rejected\",\n reason: reason,\n } as const);\n rejectedCache.set([reason], res);\n return res;\n}\nimport.meta.vitest?.test(\"rejected\", ({ expect }) => {\n // Test with error object\n const error = new Error(\"Test error\");\n const promise1 = rejected<number>(error);\n expect(promise1.status).toBe(\"rejected\");\n // Need to use type assertion since reason is only available when status is \"rejected\"\n expect((promise1 as { reason: Error }).reason).toBe(error);\n\n // Test with string reason\n const promise2 = rejected<string>(\"error message\");\n expect(promise2.status).toBe(\"rejected\");\n expect((promise2 as { reason: string }).reason).toBe(\"error message\");\n\n // Test caching (same reference for same reason)\n const promise3 = rejected<number>(error);\n expect(promise3).toBe(promise1); // Same reference due to caching\n\n // Test with different reason (different reference)\n const differentError = new Error(\"Different error\");\n const promise4 = rejected<number>(differentError);\n expect(promise4).not.toBe(promise1);\n\n // Note: We're not using await expect(promise).rejects to avoid unhandled rejections\n});\n\n// We'll skip the rejection test for pending() since it's causing unhandled rejections\n// The function is already well tested through other tests like rejected() and createPromise()\n\n\nconst neverResolvePromise = pending(new Promise<never>(() => {}));\nexport function neverResolve(): ReactPromise<never> {\n return neverResolvePromise;\n}\nimport.meta.vitest?.test(\"neverResolve\", ({ expect }) => {\n const promise = neverResolve();\n expect(promise.status).toBe(\"pending\");\n expect((promise as any).value).toBeUndefined();\n expect((promise as any).reason).toBeUndefined();\n\n // Test that multiple calls return the same promise\n const promise2 = neverResolve();\n expect(promise2).toBe(promise);\n});\n\nexport function pending<T>(promise: Promise<T>, options: { disableErrorWrapping?: boolean } = {}): ReactPromise<T> {\n const res = promise.then(\n value => {\n res.status = \"fulfilled\";\n (res as any).value = value;\n return value;\n },\n actualReason => {\n res.status = \"rejected\";\n (res as any).reason = actualReason;\n throw actualReason;\n },\n ) as ReactPromise<T>;\n res.status = \"pending\";\n return res;\n}\nimport.meta.vitest?.test(\"pending\", async ({ expect }) => {\n // Test with a promise that resolves\n const resolvePromise = Promise.resolve(42);\n const pendingPromise = pending(resolvePromise);\n\n // Initially it should be pending\n expect(pendingPromise.status).toBe(\"pending\");\n\n // After resolution, it should be fulfilled\n await resolvePromise;\n // Need to wait a tick for the then handler to execute\n await new Promise(resolve => setTimeout(resolve, 0));\n expect(pendingPromise.status).toBe(\"fulfilled\");\n expect((pendingPromise as { value: number }).value).toBe(42);\n\n // For the rejection test, we'll use a separate test to avoid unhandled rejections\n});\n\n/**\n * Should be used to wrap Promises that are not immediately awaited, so they don't throw an unhandled promise rejection\n * error.\n *\n * Vercel kills serverless functions on unhandled promise rejection errors, so this is important.\n */\nexport function ignoreUnhandledRejection<T extends Promise<any>>(promise: T): void {\n promise.catch(() => {});\n}\nimport.meta.vitest?.test(\"ignoreUnhandledRejection\", async ({ expect }) => {\n // Test with a promise that resolves\n const resolvePromise = Promise.resolve(42);\n ignoreUnhandledRejection(resolvePromise);\n expect(await resolvePromise).toBe(42); // Should still resolve to the same value\n\n // Test with a promise that rejects\n // The promise should still reject, but the rejection is caught internally\n // so it doesn't cause an unhandled rejection error\n const error = new Error(\"Test error\");\n const rejectPromise = Promise.reject(error);\n ignoreUnhandledRejection(rejectPromise);\n await expect(rejectPromise).rejects.toBe(error);\n});\n\n/**\n * See concatStacktraces for more information.\n */\nexport function concatStacktracesIfRejected<T>(promise: Promise<T>): void {\n const currentError = new Error();\n promise.catch(error => {\n if (error instanceof Error) {\n concatStacktraces(error, currentError);\n } else {\n // we can only concatenate errors, so we'll just ignore the non-error\n }\n });\n}\n\nexport async function wait(ms: number) {\n if (!Number.isFinite(ms) || ms < 0) {\n throw new HexclaveAssertionError(`wait() requires a non-negative integer number of milliseconds to wait. (found: ${ms}ms)`);\n }\n if (ms >= 2**31) {\n throw new HexclaveAssertionError(\"The maximum timeout for wait() is 2147483647ms (2**31 - 1). (found: ${ms}ms)\");\n }\n return await traceSpan({ description: 'wait(...)', attributes: { 'stack.wait.ms': ms } }, async (span) => {\n return await new Promise<void>(resolve => setTimeout(resolve, ms));\n });\n}\nimport.meta.vitest?.test(\"wait\", async ({ expect }) => {\n // Test with valid input\n const start = Date.now();\n await wait(10);\n const elapsed = Date.now() - start;\n expect(elapsed).toBeGreaterThanOrEqual(5); // Allow some flexibility in timing\n\n // Test with zero\n await expect(wait(0)).resolves.toBeUndefined();\n\n // Test with negative number\n await expect(wait(-10)).rejects.toThrow(\"wait() requires a non-negative integer\");\n\n // Test with non-finite number\n await expect(wait(NaN)).rejects.toThrow(\"wait() requires a non-negative integer\");\n await expect(wait(Infinity)).rejects.toThrow(\"wait() requires a non-negative integer\");\n\n // Test with too large number\n await expect(wait(2**31)).rejects.toThrow(\"The maximum timeout for wait()\");\n});\n\nexport async function waitUntil(date: Date) {\n return await wait(date.getTime() - Date.now());\n}\nimport.meta.vitest?.test(\"waitUntil\", async ({ expect }) => {\n // Test with future date\n const futureDate = new Date(Date.now() + 10);\n const start = Date.now();\n await waitUntil(futureDate);\n const elapsed = Date.now() - start;\n expect(elapsed).toBeGreaterThanOrEqual(5); // Allow some flexibility in timing\n\n // Test with past date - this will throw because wait() requires non-negative time\n // We need to verify it throws the correct error\n try {\n await waitUntil(new Date(Date.now() - 1000));\n expect.fail(\"Should have thrown an error\");\n } catch (error) {\n expect(error).toBeInstanceOf(HexclaveAssertionError);\n expect((error as Error).message).toContain(\"wait() requires a non-negative integer\");\n }\n});\n\nexport function runAsynchronouslyWithAlert(...args: Parameters<typeof runAsynchronously>) {\n return runAsynchronously(\n args[0],\n {\n ...args[1],\n onError: error => {\n const nodeEnv = getProcessEnv(\"NODE_ENV\");\n if (KnownError.isKnownError(error) && nodeEnv?.includes(\"production\")) {\n alert(error.message);\n } else {\n alert(`An unhandled error occurred. Please ${nodeEnv === \"development\" ? `check the browser console for the full error.` : \"report this to the developer.\"}\\n\\n${error}`);\n }\n args[1]?.onError?.(error);\n },\n },\n ...args.slice(2) as [],\n );\n}\nimport.meta.vitest?.test(\"runAsynchronouslyWithAlert\", ({ expect }) => {\n // Simple test to verify the function calls runAsynchronously\n // We can't easily test the alert functionality without mocking\n const testFn = () => Promise.resolve(\"test\");\n const testOptions = { noErrorLogging: true };\n\n // Just verify it doesn't throw\n expect(() => runAsynchronouslyWithAlert(testFn, testOptions)).not.toThrow();\n\n // We can't easily test the error handling without mocking, so we'll\n // just verify the function exists and can be called\n expect(typeof runAsynchronouslyWithAlert).toBe(\"function\");\n});\n\nexport function runAsynchronously(\n promiseOrFunc: void | Promise<unknown> | (() => void | Promise<unknown>) | undefined,\n options: {\n noErrorLogging?: boolean,\n onError?: (error: Error) => void,\n } = {},\n): void {\n if (typeof promiseOrFunc === \"function\") {\n promiseOrFunc = promiseOrFunc();\n }\n if (promiseOrFunc) {\n concatStacktracesIfRejected(promiseOrFunc);\n promiseOrFunc.catch(error => {\n options.onError?.(error);\n const newError = new HexclaveAssertionError(\n \"Uncaught error in asynchronous function: \" + errorToNiceString(error),\n { cause: error },\n );\n if (!options.noErrorLogging) {\n captureError(\"runAsynchronously\", newError);\n }\n });\n }\n}\nimport.meta.vitest?.test(\"runAsynchronously\", ({ expect }) => {\n // Simple test to verify the function exists and can be called\n const testFn = () => Promise.resolve(\"test\");\n\n // Just verify it doesn't throw\n expect(() => runAsynchronously(testFn)).not.toThrow();\n expect(() => runAsynchronously(Promise.resolve(\"test\"))).not.toThrow();\n expect(() => runAsynchronously(undefined)).not.toThrow();\n\n // We can't easily test the error handling without mocking, so we'll\n // just verify the function exists and can be called with options\n expect(() => runAsynchronously(testFn, { noErrorLogging: true })).not.toThrow();\n expect(() => runAsynchronously(testFn, { onError: () => {} })).not.toThrow();\n});\n\n\nclass TimeoutError extends Error {\n constructor(public readonly ms: number) {\n super(`Timeout after ${ms}ms`);\n this.name = \"TimeoutError\";\n }\n}\n\nexport async function timeout<T>(promiseOrFunc: Promise<T> | (() => Promise<T>), ms: number): Promise<Result<T, TimeoutError>> {\n const promise = typeof promiseOrFunc === \"function\" ? promiseOrFunc() : promiseOrFunc;\n return await Promise.race([\n promise.then(value => Result.ok(value)),\n wait(ms).then(() => Result.error(new TimeoutError(ms))),\n ]);\n}\nimport.meta.vitest?.test(\"timeout\", async ({ expect }) => {\n // Test with a promise that resolves quickly\n const fastPromise = Promise.resolve(42);\n const fastResult = await timeout(fastPromise, 100);\n expect(fastResult.status).toBe(\"ok\");\n if (fastResult.status === \"ok\") {\n expect(fastResult.data).toBe(42);\n }\n\n // Test with a promise that takes longer than the timeout\n const slowPromise = new Promise(resolve => setTimeout(() => resolve(\"too late\"), 50));\n const slowResult = await timeout(slowPromise, 10);\n expect(slowResult.status).toBe(\"error\");\n if (slowResult.status === \"error\") {\n expect(slowResult.error).toBeInstanceOf(TimeoutError);\n expect((slowResult.error as TimeoutError).ms).toBe(10);\n }\n});\n\nexport async function timeoutThrow<T>(promise: Promise<T>, ms: number): Promise<T> {\n return Result.orThrow(await timeout(promise, ms));\n}\nimport.meta.vitest?.test(\"timeoutThrow\", async ({ expect }) => {\n // Test with a promise that resolves quickly\n const fastPromise = Promise.resolve(42);\n const fastResult = await timeoutThrow(fastPromise, 100);\n expect(fastResult).toBe(42);\n\n // Test with a promise that takes longer than the timeout\n const slowPromise = new Promise(resolve => setTimeout(() => resolve(\"too late\"), 50));\n await expect(timeoutThrow(slowPromise, 10)).rejects.toThrow(\"Timeout after 10ms\");\n await expect(timeoutThrow(slowPromise, 10)).rejects.toBeInstanceOf(TimeoutError);\n});\n\n\n/**\n * Maps over `items` with `fn`, running at most `concurrency` invocations at a time.\n *\n * Unlike `Promise.all(items.map(fn))`, this bounds the number of in-flight\n * promises, which matters when `fn` hits a shared resource (e.g. a database) and\n * an unbounded fan-out could exhaust connections or overload a replica. Results\n * are returned in input order regardless of completion order, and the first\n * rejection aborts further scheduling — already in-flight workers still settle\n * but no new items are started.\n */\nexport async function mapWithConcurrency<T, R>(\n items: readonly T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n if (!Number.isInteger(concurrency) || concurrency < 1) {\n throw new HexclaveAssertionError(`mapWithConcurrency requires a positive integer concurrency, got ${concurrency}`);\n }\n const results = new Array<R>(items.length);\n let nextIndex = 0;\n let aborted = false;\n const worker = async () => {\n while (!aborted) {\n // Claim an index synchronously before awaiting so workers never process the same item.\n const index = nextIndex++;\n if (index >= items.length) return;\n try {\n // Bounds-checked above; `?? throwErr(…)` is unsuitable because T may legitimately be null/undefined\n results[index] = await fn(items[index] as T, index);\n } catch (error) {\n aborted = true;\n throw error;\n }\n }\n };\n const workerCount = Math.min(concurrency, items.length);\n await Promise.all(Array.from({ length: workerCount }, () => worker()));\n return results;\n}\nimport.meta.vitest?.test(\"mapWithConcurrency\", async ({ expect }) => {\n // Preserves input order regardless of completion order.\n const ordered = await mapWithConcurrency([30, 10, 20], 3, async (ms, index) => {\n await wait(ms);\n return `${index}:${ms}`;\n });\n expect(ordered).toEqual([\"0:30\", \"1:10\", \"2:20\"]);\n\n // Never exceeds the configured concurrency.\n let inFlight = 0;\n let maxInFlight = 0;\n await mapWithConcurrency(Array.from({ length: 10 }, (_, i) => i), 3, async () => {\n inFlight++;\n maxInFlight = Math.max(maxInFlight, inFlight);\n await wait(5);\n inFlight--;\n });\n expect(maxInFlight).toBe(3);\n\n // Empty input spawns no workers and returns an empty array.\n expect(await mapWithConcurrency([], 4, async () => 1)).toEqual([]);\n\n // Invalid concurrency fails loudly.\n await expect(mapWithConcurrency([1], 0, async (x) => x)).rejects.toThrow(\"positive integer concurrency\");\n});\n\nexport type RateLimitOptions = {\n /**\n * The number of requests to process in parallel. Currently only 1 is supported.\n */\n concurrency: 1,\n\n /**\n * If true, multiple requests waiting at the same time will be reduced to just one. Default is false.\n */\n batchCalls?: boolean,\n\n /**\n * Waits for throttleMs since the start of last request before starting the next request. Default is 0.\n */\n throttleMs?: number,\n\n /**\n * Waits for gapMs since the end of last request before starting the next request. Default is 0.\n */\n gapMs?: number,\n\n /**\n * Waits until there have been no new requests for debounceMs before starting a new request. Default is 0.\n */\n debounceMs?: number,\n};\n\nexport function rateLimited<T>(\n func: () => Promise<T>,\n options: RateLimitOptions,\n): () => Promise<T> {\n let waitUntil = performance.now();\n let queue: [(t: T) => void, (e: unknown) => void][] = [];\n let addedToQueueCallbacks = new Map<string, () => void>;\n\n const next = async () => {\n while (true) {\n if (waitUntil > performance.now()) {\n await wait(Math.max(1, waitUntil - performance.now() + 1));\n } else if (queue.length === 0) {\n const uuid = generateUuid();\n await new Promise<void>(resolve => {\n addedToQueueCallbacks.set(uuid, resolve);\n });\n addedToQueueCallbacks.delete(uuid);\n } else {\n break;\n }\n }\n const nextFuncs = options.batchCalls ? queue.splice(0, queue.length) : [queue.shift()!];\n\n const start = performance.now();\n const value = await Result.fromPromise(func());\n const end = performance.now();\n\n waitUntil = Math.max(\n waitUntil,\n start + (options.throttleMs ?? 0),\n end + (options.gapMs ?? 0),\n );\n\n for (const nextFunc of nextFuncs) {\n if (value.status === \"ok\") {\n nextFunc[0](value.data);\n } else {\n nextFunc[1](value.error);\n }\n }\n };\n\n runAsynchronously(async () => {\n while (true) {\n await next();\n }\n });\n\n return () => {\n return new Promise<T>((resolve, reject) => {\n waitUntil = Math.max(\n waitUntil,\n performance.now() + (options.debounceMs ?? 0),\n );\n queue.push([resolve, reject]);\n addedToQueueCallbacks.forEach(cb => cb());\n });\n };\n}\n\nexport function throttled<T, A extends any[]>(func: (...args: A) => Promise<T>, delayMs: number): (...args: A) => Promise<T> {\n let timeout: ReturnType<typeof setTimeout> | null = null;\n let nextAvailable: Promise<T> | null = null;\n return async (...args) => {\n while (nextAvailable !== null) {\n await nextAvailable;\n }\n nextAvailable = new Promise<T>(resolve => {\n timeout = setTimeout(() => {\n nextAvailable = null;\n resolve(func(...args));\n }, delayMs);\n });\n return await nextAvailable;\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"promises.js","names":["DependenciesMap","HexclaveAssertionError","KnownError","Result"],"sources":["../../src/utils/promises.tsx"],"sourcesContent":["import { KnownError } from \"..\";\nimport { getProcessEnv } from \"./env\";\nimport { HexclaveAssertionError, captureError, concatStacktraces, errorToNiceString } from \"./errors\";\nimport { DependenciesMap } from \"./maps\";\nimport { Result } from \"./results\";\nimport { traceSpan } from \"./telemetry\";\nimport { generateUuid } from \"./uuids\";\n\nexport type ReactPromise<T> = Promise<T> & (\n | { status: \"rejected\", reason: unknown }\n | { status: \"fulfilled\", value: T }\n | { status: \"pending\" }\n);\n\ntype Resolve<T> = (value: T) => void;\ntype Reject = (reason: unknown) => void;\nexport function createPromise<T>(callback: (resolve: Resolve<T>, reject: Reject) => void): ReactPromise<T> {\n let status = \"pending\" as \"fulfilled\" | \"rejected\" | \"pending\";\n let valueOrReason: T | unknown | undefined = undefined;\n let resolve: Resolve<T> | null = null;\n let reject: Reject | null = null;\n const promise = new Promise<T>((res, rej) => {\n resolve = (value) => {\n if (status !== \"pending\") return;\n status = \"fulfilled\";\n valueOrReason = value;\n res(value);\n };\n reject = (reason) => {\n if (status !== \"pending\") return;\n status = \"rejected\";\n valueOrReason = reason;\n rej(reason);\n };\n });\n\n callback(resolve!, reject!);\n return Object.assign(promise, {\n status: status,\n ...status === \"fulfilled\" ? { value: valueOrReason as T } : {},\n ...status === \"rejected\" ? { reason: valueOrReason } : {},\n } as any);\n}\nimport.meta.vitest?.test(\"createPromise\", async ({ expect }) => {\n // Test resolved promise\n const resolvedPromise = createPromise<number>((resolve) => {\n resolve(42);\n });\n expect(resolvedPromise.status).toBe(\"fulfilled\");\n expect((resolvedPromise as any).value).toBe(42);\n expect(await resolvedPromise).toBe(42);\n\n // Test rejected promise\n const error = new Error(\"Test error\");\n const rejectedPromise = createPromise<number>((_, reject) => {\n reject(error);\n });\n expect(rejectedPromise.status).toBe(\"rejected\");\n expect((rejectedPromise as any).reason).toBe(error);\n await expect(rejectedPromise).rejects.toBe(error);\n\n // Test pending promise\n const pendingPromise = createPromise<number>(() => {\n // Do nothing, leave it pending\n });\n expect(pendingPromise.status).toBe(\"pending\");\n expect((pendingPromise as any).value).toBeUndefined();\n expect((pendingPromise as any).reason).toBeUndefined();\n\n // Test that resolving after already resolved does nothing\n let resolveCount = 0;\n const multiResolvePromise = createPromise<number>((resolve) => {\n resolve(1);\n resolveCount++;\n resolve(2);\n resolveCount++;\n });\n expect(resolveCount).toBe(2); // Both resolve calls executed\n expect(multiResolvePromise.status).toBe(\"fulfilled\");\n expect((multiResolvePromise as any).value).toBe(1); // Only first resolve took effect\n expect(await multiResolvePromise).toBe(1);\n});\n\nlet resolvedCache: DependenciesMap<[unknown], ReactPromise<unknown>> | null = null;\n/**\n * Like Promise.resolve(...), but also adds the status and value properties for use with React's `use` hook, and caches\n * the value so that invoking `resolved` twice returns the same promise.\n */\nexport function resolved<T>(value: T): ReactPromise<T> {\n resolvedCache ??= new DependenciesMap<[unknown], ReactPromise<unknown>>();\n if (resolvedCache.has([value])) {\n return resolvedCache.get([value]) as ReactPromise<T>;\n }\n\n const res = Object.assign(Promise.resolve(value), {\n status: \"fulfilled\",\n value,\n } as const);\n resolvedCache.set([value], res);\n return res;\n}\nimport.meta.vitest?.test(\"resolved\", async ({ expect }) => {\n // Test with primitive value\n const promise1 = resolved(42);\n expect(promise1.status).toBe(\"fulfilled\");\n // Need to use type assertion since value is only available when status is \"fulfilled\"\n expect((promise1 as { value: number }).value).toBe(42);\n expect(await promise1).toBe(42);\n\n // Test with object value\n const obj = { test: true };\n const promise2 = resolved(obj);\n expect(promise2.status).toBe(\"fulfilled\");\n expect((promise2 as { value: typeof obj }).value).toBe(obj);\n expect(await promise2).toBe(obj);\n\n // Test caching (same reference for same value)\n const promise3 = resolved(42);\n expect(promise3).toBe(promise1); // Same reference due to caching\n\n // Test with different value (different reference)\n const promise4 = resolved(43);\n expect(promise4).not.toBe(promise1);\n});\n\nlet rejectedCache: DependenciesMap<[unknown], ReactPromise<unknown>> | null = null;\n/**\n * Like Promise.reject(...), but also adds the status and value properties for use with React's `use` hook, and caches\n * the value so that invoking `rejected` twice returns the same promise.\n */\nexport function rejected<T>(reason: unknown): ReactPromise<T> {\n rejectedCache ??= new DependenciesMap<[unknown], ReactPromise<unknown>>();\n if (rejectedCache.has([reason])) {\n return rejectedCache.get([reason]) as ReactPromise<T>;\n }\n\n const promise = Promise.reject(reason);\n ignoreUnhandledRejection(promise);\n const res = Object.assign(promise, {\n status: \"rejected\",\n reason: reason,\n } as const);\n rejectedCache.set([reason], res);\n return res;\n}\nimport.meta.vitest?.test(\"rejected\", ({ expect }) => {\n // Test with error object\n const error = new Error(\"Test error\");\n const promise1 = rejected<number>(error);\n expect(promise1.status).toBe(\"rejected\");\n // Need to use type assertion since reason is only available when status is \"rejected\"\n expect((promise1 as { reason: Error }).reason).toBe(error);\n\n // Test with string reason\n const promise2 = rejected<string>(\"error message\");\n expect(promise2.status).toBe(\"rejected\");\n expect((promise2 as { reason: string }).reason).toBe(\"error message\");\n\n // Test caching (same reference for same reason)\n const promise3 = rejected<number>(error);\n expect(promise3).toBe(promise1); // Same reference due to caching\n\n // Test with different reason (different reference)\n const differentError = new Error(\"Different error\");\n const promise4 = rejected<number>(differentError);\n expect(promise4).not.toBe(promise1);\n\n // Note: We're not using await expect(promise).rejects to avoid unhandled rejections\n});\n\n// We'll skip the rejection test for pending() since it's causing unhandled rejections\n// The function is already well tested through other tests like rejected() and createPromise()\n\n\nconst neverResolvePromise = pending(new Promise<never>(() => {}));\nexport function neverResolve(): ReactPromise<never> {\n return neverResolvePromise;\n}\nimport.meta.vitest?.test(\"neverResolve\", ({ expect }) => {\n const promise = neverResolve();\n expect(promise.status).toBe(\"pending\");\n expect((promise as any).value).toBeUndefined();\n expect((promise as any).reason).toBeUndefined();\n\n // Test that multiple calls return the same promise\n const promise2 = neverResolve();\n expect(promise2).toBe(promise);\n});\n\nexport function pending<T>(promise: Promise<T>, options: { disableErrorWrapping?: boolean } = {}): ReactPromise<T> {\n const res = promise.then(\n value => {\n res.status = \"fulfilled\";\n (res as any).value = value;\n return value;\n },\n actualReason => {\n res.status = \"rejected\";\n (res as any).reason = actualReason;\n throw actualReason;\n },\n ) as ReactPromise<T>;\n res.status = \"pending\";\n return res;\n}\nimport.meta.vitest?.test(\"pending\", async ({ expect }) => {\n // Test with a promise that resolves\n const resolvePromise = Promise.resolve(42);\n const pendingPromise = pending(resolvePromise);\n\n // Initially it should be pending\n expect(pendingPromise.status).toBe(\"pending\");\n\n // After resolution, it should be fulfilled\n await resolvePromise;\n // Need to wait a tick for the then handler to execute\n await new Promise(resolve => setTimeout(resolve, 0));\n expect(pendingPromise.status).toBe(\"fulfilled\");\n expect((pendingPromise as { value: number }).value).toBe(42);\n\n // For the rejection test, we'll use a separate test to avoid unhandled rejections\n});\n\n/**\n * Should be used to wrap Promises that are not immediately awaited, so they don't throw an unhandled promise rejection\n * error.\n *\n * Vercel kills serverless functions on unhandled promise rejection errors, so this is important.\n */\nexport function ignoreUnhandledRejection<T extends Promise<any>>(promise: T): void {\n promise.catch(() => {});\n}\nimport.meta.vitest?.test(\"ignoreUnhandledRejection\", async ({ expect }) => {\n // Test with a promise that resolves\n const resolvePromise = Promise.resolve(42);\n ignoreUnhandledRejection(resolvePromise);\n expect(await resolvePromise).toBe(42); // Should still resolve to the same value\n\n // Test with a promise that rejects\n // The promise should still reject, but the rejection is caught internally\n // so it doesn't cause an unhandled rejection error\n const error = new Error(\"Test error\");\n const rejectPromise = Promise.reject(error);\n ignoreUnhandledRejection(rejectPromise);\n await expect(rejectPromise).rejects.toBe(error);\n});\n\n/**\n * See concatStacktraces for more information.\n */\nexport function concatStacktracesIfRejected<T>(promise: Promise<T>): void {\n const currentError = new Error();\n promise.catch(error => {\n if (error instanceof Error) {\n concatStacktraces(error, currentError);\n } else {\n // we can only concatenate errors, so we'll just ignore the non-error\n }\n });\n}\n\nexport async function wait(ms: number, options?: { unref?: boolean }) {\n if (!Number.isFinite(ms) || ms < 0) {\n throw new HexclaveAssertionError(`wait() requires a non-negative integer number of milliseconds to wait. (found: ${ms}ms)`);\n }\n if (ms >= 2**31) {\n throw new HexclaveAssertionError(\"The maximum timeout for wait() is 2147483647ms (2**31 - 1). (found: ${ms}ms)\");\n }\n return await traceSpan({ description: 'wait(...)', attributes: { 'stack.wait.ms': ms } }, async (span) => {\n return await new Promise<void>((resolve) => {\n const timeout = setTimeout(resolve, ms);\n if (options?.unref === true && typeof timeout === \"object\") timeout.unref();\n });\n });\n}\nimport.meta.vitest?.test(\"wait\", async ({ expect }) => {\n // Test with valid input\n const start = Date.now();\n await wait(10);\n const elapsed = Date.now() - start;\n expect(elapsed).toBeGreaterThanOrEqual(5); // Allow some flexibility in timing\n\n // Test with zero\n await expect(wait(0)).resolves.toBeUndefined();\n\n // Node's unref option must preserve the normal wait behavior while another\n // referenced handle (the test runner) owns the process lifetime.\n await expect(wait(0, { unref: true })).resolves.toBeUndefined();\n\n // Test with negative number\n await expect(wait(-10)).rejects.toThrow(\"wait() requires a non-negative integer\");\n\n // Test with non-finite number\n await expect(wait(NaN)).rejects.toThrow(\"wait() requires a non-negative integer\");\n await expect(wait(Infinity)).rejects.toThrow(\"wait() requires a non-negative integer\");\n\n // Test with too large number\n await expect(wait(2**31)).rejects.toThrow(\"The maximum timeout for wait()\");\n});\n\nexport async function waitUntil(date: Date) {\n return await wait(date.getTime() - Date.now());\n}\nimport.meta.vitest?.test(\"waitUntil\", async ({ expect }) => {\n // Test with future date\n const futureDate = new Date(Date.now() + 10);\n const start = Date.now();\n await waitUntil(futureDate);\n const elapsed = Date.now() - start;\n expect(elapsed).toBeGreaterThanOrEqual(5); // Allow some flexibility in timing\n\n // Test with past date - this will throw because wait() requires non-negative time\n // We need to verify it throws the correct error\n try {\n await waitUntil(new Date(Date.now() - 1000));\n expect.fail(\"Should have thrown an error\");\n } catch (error) {\n expect(error).toBeInstanceOf(HexclaveAssertionError);\n expect((error as Error).message).toContain(\"wait() requires a non-negative integer\");\n }\n});\n\nexport function runAsynchronouslyWithAlert(...args: Parameters<typeof runAsynchronously>) {\n return runAsynchronously(\n args[0],\n {\n ...args[1],\n onError: error => {\n const nodeEnv = getProcessEnv(\"NODE_ENV\");\n if (KnownError.isKnownError(error) && nodeEnv?.includes(\"production\")) {\n alert(error.message);\n } else {\n alert(`An unhandled error occurred. Please ${nodeEnv === \"development\" ? `check the browser console for the full error.` : \"report this to the developer.\"}\\n\\n${error}`);\n }\n args[1]?.onError?.(error);\n },\n },\n ...args.slice(2) as [],\n );\n}\nimport.meta.vitest?.test(\"runAsynchronouslyWithAlert\", ({ expect }) => {\n // Simple test to verify the function calls runAsynchronously\n // We can't easily test the alert functionality without mocking\n const testFn = () => Promise.resolve(\"test\");\n const testOptions = { noErrorLogging: true };\n\n // Just verify it doesn't throw\n expect(() => runAsynchronouslyWithAlert(testFn, testOptions)).not.toThrow();\n\n // We can't easily test the error handling without mocking, so we'll\n // just verify the function exists and can be called\n expect(typeof runAsynchronouslyWithAlert).toBe(\"function\");\n});\n\nexport function runAsynchronously(\n promiseOrFunc: void | Promise<unknown> | (() => void | Promise<unknown>) | undefined,\n options: {\n noErrorLogging?: boolean,\n onError?: (error: Error) => void,\n } = {},\n): void {\n if (typeof promiseOrFunc === \"function\") {\n promiseOrFunc = promiseOrFunc();\n }\n if (promiseOrFunc) {\n concatStacktracesIfRejected(promiseOrFunc);\n promiseOrFunc.catch(error => {\n options.onError?.(error);\n const newError = new HexclaveAssertionError(\n \"Uncaught error in asynchronous function: \" + errorToNiceString(error),\n { cause: error },\n );\n if (!options.noErrorLogging) {\n captureError(\"runAsynchronously\", newError);\n }\n });\n }\n}\nimport.meta.vitest?.test(\"runAsynchronously\", ({ expect }) => {\n // Simple test to verify the function exists and can be called\n const testFn = () => Promise.resolve(\"test\");\n\n // Just verify it doesn't throw\n expect(() => runAsynchronously(testFn)).not.toThrow();\n expect(() => runAsynchronously(Promise.resolve(\"test\"))).not.toThrow();\n expect(() => runAsynchronously(undefined)).not.toThrow();\n\n // We can't easily test the error handling without mocking, so we'll\n // just verify the function exists and can be called with options\n expect(() => runAsynchronously(testFn, { noErrorLogging: true })).not.toThrow();\n expect(() => runAsynchronously(testFn, { onError: () => {} })).not.toThrow();\n});\n\n\nclass TimeoutError extends Error {\n constructor(public readonly ms: number) {\n super(`Timeout after ${ms}ms`);\n this.name = \"TimeoutError\";\n }\n}\n\nexport async function timeout<T>(promiseOrFunc: Promise<T> | (() => Promise<T>), ms: number): Promise<Result<T, TimeoutError>> {\n const promise = typeof promiseOrFunc === \"function\" ? promiseOrFunc() : promiseOrFunc;\n return await Promise.race([\n promise.then(value => Result.ok(value)),\n wait(ms).then(() => Result.error(new TimeoutError(ms))),\n ]);\n}\nimport.meta.vitest?.test(\"timeout\", async ({ expect }) => {\n // Test with a promise that resolves quickly\n const fastPromise = Promise.resolve(42);\n const fastResult = await timeout(fastPromise, 100);\n expect(fastResult.status).toBe(\"ok\");\n if (fastResult.status === \"ok\") {\n expect(fastResult.data).toBe(42);\n }\n\n // Test with a promise that takes longer than the timeout\n const slowPromise = new Promise(resolve => setTimeout(() => resolve(\"too late\"), 50));\n const slowResult = await timeout(slowPromise, 10);\n expect(slowResult.status).toBe(\"error\");\n if (slowResult.status === \"error\") {\n expect(slowResult.error).toBeInstanceOf(TimeoutError);\n expect((slowResult.error as TimeoutError).ms).toBe(10);\n }\n});\n\nexport async function timeoutThrow<T>(promise: Promise<T>, ms: number): Promise<T> {\n return Result.orThrow(await timeout(promise, ms));\n}\nimport.meta.vitest?.test(\"timeoutThrow\", async ({ expect }) => {\n // Test with a promise that resolves quickly\n const fastPromise = Promise.resolve(42);\n const fastResult = await timeoutThrow(fastPromise, 100);\n expect(fastResult).toBe(42);\n\n // Test with a promise that takes longer than the timeout\n const slowPromise = new Promise(resolve => setTimeout(() => resolve(\"too late\"), 50));\n await expect(timeoutThrow(slowPromise, 10)).rejects.toThrow(\"Timeout after 10ms\");\n await expect(timeoutThrow(slowPromise, 10)).rejects.toBeInstanceOf(TimeoutError);\n});\n\n\n/**\n * Maps over `items` with `fn`, running at most `concurrency` invocations at a time.\n *\n * Unlike `Promise.all(items.map(fn))`, this bounds the number of in-flight\n * promises, which matters when `fn` hits a shared resource (e.g. a database) and\n * an unbounded fan-out could exhaust connections or overload a replica. Results\n * are returned in input order regardless of completion order, and the first\n * rejection aborts further scheduling — already in-flight workers still settle\n * but no new items are started.\n */\nexport async function mapWithConcurrency<T, R>(\n items: readonly T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n if (!Number.isInteger(concurrency) || concurrency < 1) {\n throw new HexclaveAssertionError(`mapWithConcurrency requires a positive integer concurrency, got ${concurrency}`);\n }\n const results = new Array<R>(items.length);\n let nextIndex = 0;\n let aborted = false;\n const worker = async () => {\n while (!aborted) {\n // Claim an index synchronously before awaiting so workers never process the same item.\n const index = nextIndex++;\n if (index >= items.length) return;\n try {\n // Bounds-checked above; `?? throwErr(…)` is unsuitable because T may legitimately be null/undefined\n results[index] = await fn(items[index] as T, index);\n } catch (error) {\n aborted = true;\n throw error;\n }\n }\n };\n const workerCount = Math.min(concurrency, items.length);\n await Promise.all(Array.from({ length: workerCount }, () => worker()));\n return results;\n}\nimport.meta.vitest?.test(\"mapWithConcurrency\", async ({ expect }) => {\n // Preserves input order regardless of completion order.\n const ordered = await mapWithConcurrency([30, 10, 20], 3, async (ms, index) => {\n await wait(ms);\n return `${index}:${ms}`;\n });\n expect(ordered).toEqual([\"0:30\", \"1:10\", \"2:20\"]);\n\n // Never exceeds the configured concurrency.\n let inFlight = 0;\n let maxInFlight = 0;\n await mapWithConcurrency(Array.from({ length: 10 }, (_, i) => i), 3, async () => {\n inFlight++;\n maxInFlight = Math.max(maxInFlight, inFlight);\n await wait(5);\n inFlight--;\n });\n expect(maxInFlight).toBe(3);\n\n // Empty input spawns no workers and returns an empty array.\n expect(await mapWithConcurrency([], 4, async () => 1)).toEqual([]);\n\n // Invalid concurrency fails loudly.\n await expect(mapWithConcurrency([1], 0, async (x) => x)).rejects.toThrow(\"positive integer concurrency\");\n});\n\nexport type RateLimitOptions = {\n /**\n * The number of requests to process in parallel. Currently only 1 is supported.\n */\n concurrency: 1,\n\n /**\n * If true, multiple requests waiting at the same time will be reduced to just one. Default is false.\n */\n batchCalls?: boolean,\n\n /**\n * Waits for throttleMs since the start of last request before starting the next request. Default is 0.\n */\n throttleMs?: number,\n\n /**\n * Waits for gapMs since the end of last request before starting the next request. Default is 0.\n */\n gapMs?: number,\n\n /**\n * Waits until there have been no new requests for debounceMs before starting a new request. Default is 0.\n */\n debounceMs?: number,\n};\n\nexport function rateLimited<T>(\n func: () => Promise<T>,\n options: RateLimitOptions,\n): () => Promise<T> {\n let waitUntil = performance.now();\n let queue: [(t: T) => void, (e: unknown) => void][] = [];\n let addedToQueueCallbacks = new Map<string, () => void>;\n\n const next = async () => {\n while (true) {\n if (waitUntil > performance.now()) {\n await wait(Math.max(1, waitUntil - performance.now() + 1));\n } else if (queue.length === 0) {\n const uuid = generateUuid();\n await new Promise<void>(resolve => {\n addedToQueueCallbacks.set(uuid, resolve);\n });\n addedToQueueCallbacks.delete(uuid);\n } else {\n break;\n }\n }\n const nextFuncs = options.batchCalls ? queue.splice(0, queue.length) : [queue.shift()!];\n\n const start = performance.now();\n const value = await Result.fromPromise(func());\n const end = performance.now();\n\n waitUntil = Math.max(\n waitUntil,\n start + (options.throttleMs ?? 0),\n end + (options.gapMs ?? 0),\n );\n\n for (const nextFunc of nextFuncs) {\n if (value.status === \"ok\") {\n nextFunc[0](value.data);\n } else {\n nextFunc[1](value.error);\n }\n }\n };\n\n runAsynchronously(async () => {\n while (true) {\n await next();\n }\n });\n\n return () => {\n return new Promise<T>((resolve, reject) => {\n waitUntil = Math.max(\n waitUntil,\n performance.now() + (options.debounceMs ?? 0),\n );\n queue.push([resolve, reject]);\n addedToQueueCallbacks.forEach(cb => cb());\n });\n };\n}\n\nexport function throttled<T, A extends any[]>(func: (...args: A) => Promise<T>, delayMs: number): (...args: A) => Promise<T> {\n let timeout: ReturnType<typeof setTimeout> | null = null;\n let nextAvailable: Promise<T> | null = null;\n return async (...args) => {\n while (nextAvailable !== null) {\n await nextAvailable;\n }\n nextAvailable = new Promise<T>(resolve => {\n timeout = setTimeout(() => {\n nextAvailable = null;\n resolve(func(...args));\n }, delayMs);\n });\n return await nextAvailable;\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -28,7 +28,9 @@ export const remindersPrompt = deindent`
|
|
|
28
28
|
- Hexclave also has a REST API with near-full feature parity with the SDK. It can be used for both client and server-side code.
|
|
29
29
|
- If available, always prefer editing the \`hexclave.config.ts\` file directly over asking the user to make changes on the dashboard. When implementing new features, you can always update the config file, and then tell the user about the changes you've made. The config file is automatically synced when using the local dashboard/dev environment with \`npx @hexclave/cli dev --config-file <path-to-config-file>\`.
|
|
30
30
|
- While allowed, avoid using nested property notation in Hexclave's config files for stylistic reasons. For example, instead of the config \`{ auth: { allowSignUp: true, password: { allowSignIn: true } } }\`, use config \`{ auth: { allowSignUp: true }, "auth.password": { allowSignIn: true } }\`.
|
|
31
|
+
- For new projects, prefer \`urls: { default: { type: "hosted" } }\` over the old \`type: "handler"\`. The latter uses URLs like \`/handler/sign-in\`, but the new flow instead redirects to hosted component pages, which are more user-friendly and update automatically. The old handler option requires the StackHandler component, which is only available in some frameworks and less flexible. The new hosted components flow does NOT have a URL like \`/handler/sign-in\` anymore.
|
|
31
32
|
- You can use the \`npx @hexclave/cli exec <javascript>\` command to run JavaScript with a pre-configured HexclaveServerApp available as \`hexclaveServerApp\`. This allows you to read and write from and to the Hexclave project as you would on the dashboard, but from the CLI. To read and write project configuration, see the note on the config file above.
|
|
32
33
|
- For advanced read queries, you can use \`hexclaveServerApp.queryAnalytics("<clickhouse-sql>")\`. Use \`SHOW TABLES\` and \`DESCRIBE TABLE\` to understand the schema of the available tables (columns have comments that may be useful as a description).
|
|
33
34
|
- Hexclave was formerly known as Stack Auth. You may still see references to it as Stack Auth in some places.
|
|
35
|
+
- When a human is talking about Users, more often than not, they are referring to non-anonymous users. Make sure to decide whether to filter anonymous users out in SQL queries based on suspected intent.
|
|
34
36
|
`;
|
|
@@ -559,7 +559,9 @@ const appSetupPrompt: Record<PublicAppSetupPromptId, string> =
|
|
|
559
559
|
};
|
|
560
560
|
\`\`\`
|
|
561
561
|
|
|
562
|
-
|
|
562
|
+
For some OAuth providers like Google and GitHub, Hexclave automatically provides a client ID and secret, so no extra provider setup is needed.
|
|
563
|
+
|
|
564
|
+
Then wire the SDK setup above: create the Hexclave App object, wrap React apps in the provider, and add auth pages where your framework needs them. OAuth client IDs/secrets and trusted domains are environment-specific, so leave placeholders or ask the user for those instead of inventing them. See [Auth providers](https://docs.hexclave.com/guides/apps/authentication/auth-providers) and [hexclave.config.ts: Auth](https://docs.hexclave.com/guides/going-further/hexclave-config#auth).
|
|
563
565
|
`,
|
|
564
566
|
"fraud-protection": deindent`
|
|
565
567
|
Key concepts: sign-up rules are ordered checks that decide whether a sign-up should be allowed, rejected, restricted, or logged; rule priority decides which matching rule wins when multiple rules apply.
|
|
@@ -782,6 +784,7 @@ export function getSdkSetupPrompt(mainType: "ai-prompt" | "nextjs" | "react" | "
|
|
|
782
784
|
const isDefinitelyVanillaReact = mainType === "react";
|
|
783
785
|
const isMaybeVanillaReact = isDefinitelyVanillaReact || mainType === "ai-prompt";
|
|
784
786
|
const isDefinitelyVite = isDefinitelyTanstackStart;
|
|
787
|
+
const isMaybeNoBundler = mainType === "js" || mainType === "ai-prompt";
|
|
785
788
|
|
|
786
789
|
const isDefinitelyBackend = mainType === "nodejs" || mainType === "bun" || mainType === "nextjs";
|
|
787
790
|
const isMaybeBackend = isDefinitelyBackend || mainType === "js" || mainType === "ai-prompt";
|
|
@@ -840,7 +843,7 @@ export function getSdkSetupPrompt(mainType: "ai-prompt" | "nextjs" | "react" | "
|
|
|
840
843
|
- React: \`@hexclave/react\`
|
|
841
844
|
- TanStack Start: \`@hexclave/tanstack-start\`
|
|
842
845
|
- Other & vanilla JS: \`@hexclave/js\`
|
|
843
|
-
- Vanilla JS in browser with no bundler: Cannot use npm packages, so use ESM imports with \`https://esm.sh/@hexclave/js\`
|
|
846
|
+
- Vanilla JS in browser with no bundler: Cannot use npm packages, so use ESM imports with \`https://esm.sh/@hexclave/js\` in a \`<script type="module">\` tag (see the "Browser \`<script>\` tag" section below). This is significantly less preferred than installing the npm package with a bundler, and environment variables do not work with it.
|
|
844
847
|
|
|
845
848
|
You can install the correct JavaScript Hexclave SDK into your project by running the following command:
|
|
846
849
|
` : deindent`
|
|
@@ -875,6 +878,44 @@ export function getSdkSetupPrompt(mainType: "ai-prompt" | "nextjs" | "react" | "
|
|
|
875
878
|
\`\`\`
|
|
876
879
|
` : ""}
|
|
877
880
|
|
|
881
|
+
${isMaybeFrontend && isMaybeNoBundler ? deindent`
|
|
882
|
+
#### Browser \`<script>\` tag (no bundler)
|
|
883
|
+
|
|
884
|
+
<Note>
|
|
885
|
+
This approach is significantly less preferred than installing the \`@hexclave/js\` npm package and using a bundler. Reach for it only for quick prototypes, static HTML pages, or environments where you genuinely cannot run a build step. Prefer the npm setup above whenever possible.
|
|
886
|
+
</Note>
|
|
887
|
+
|
|
888
|
+
If you cannot use npm or a bundler, load the SDK directly from [esm.sh](https://esm.sh) inside a \`<script type="module">\` tag and expose the app on the global scope:
|
|
889
|
+
|
|
890
|
+
\`\`\`html
|
|
891
|
+
<script type="module">
|
|
892
|
+
// Pin a specific version so a new release cannot unexpectedly break your page.
|
|
893
|
+
import { HexclaveClientApp } from "https://esm.sh/@hexclave/js@1.0.51";
|
|
894
|
+
|
|
895
|
+
globalThis.hexclaveClientApp = new HexclaveClientApp({
|
|
896
|
+
// Environment variables are NOT read with this approach, so the project ID
|
|
897
|
+
// (and the publishable client key, if the project has requirePublishableClientKey
|
|
898
|
+
// enabled) MUST be passed explicitly here.
|
|
899
|
+
projectId: "your-project-id",
|
|
900
|
+
tokenStore: "cookie",
|
|
901
|
+
urls: {
|
|
902
|
+
default: {
|
|
903
|
+
type: "hosted",
|
|
904
|
+
},
|
|
905
|
+
},
|
|
906
|
+
});
|
|
907
|
+
</script>
|
|
908
|
+
\`\`\`
|
|
909
|
+
|
|
910
|
+
Any other script on the page can then use the app through the global, for example \`await globalThis.hexclaveClientApp.getUser()\`.
|
|
911
|
+
|
|
912
|
+
Important caveats for this approach:
|
|
913
|
+
|
|
914
|
+
- **Environment variables do not work.** There is no build step to inject \`HEXCLAVE_PROJECT_ID\` and related variables, so you must hard-code \`projectId\` (and \`publishableClientKey\` if \`requirePublishableClientKey\` is enabled) directly in the constructor.
|
|
915
|
+
- Only ever construct a \`HexclaveClientApp\` here, never a \`HexclaveServerApp\`, since everything in a \`<script>\` tag is publicly visible. Do not put a secret server key on the page.
|
|
916
|
+
- As shown above, pin a specific version (e.g. \`https://esm.sh/@hexclave/js@1.0.51\`) rather than the unpinned \`https://esm.sh/@hexclave/js\`, so that a new SDK release cannot unexpectedly break your page.
|
|
917
|
+
` : ""}
|
|
918
|
+
|
|
878
919
|
${isMaybeBackend ? deindent`
|
|
879
920
|
In a backend where you can keep a secret key safe, you can use the \`HexclaveServerApp\`, which provides access to more sensitive APIs compared to \`HexclaveClientApp\`:
|
|
880
921
|
|
|
@@ -966,7 +1007,7 @@ export function getSdkSetupPrompt(mainType: "ai-prompt" | "nextjs" | "react" | "
|
|
|
966
1007
|
}
|
|
967
1008
|
\`\`\`
|
|
968
1009
|
|
|
969
|
-
\`hexclave dev\` injects all necessary environment variables into the app process automatically, so the app is ready to use without any extra environment variable setup.${isAiPrompt ? " It injects non-sensitive environment variables (eg. the project ID) with and without the prefixes \`NEXT_PUBLIC_\` and \`VITE_\`, so no extra environment variable setup is necessary for most frameworks." : ""}
|
|
1010
|
+
\`hexclave dev\` injects all necessary environment variables into the app process automatically, so the app is ready to use without any extra environment variable setup.${isAiPrompt ? " It injects non-sensitive environment variables (eg. the project ID) with and without the prefixes \`NEXT_PUBLIC_\` and \`VITE_\`, so no extra environment variable setup is necessary for most frameworks. Do not run `npm run dev:inner`, as that will skip past the Hexclave server." : ""}
|
|
970
1011
|
</Accordion>
|
|
971
1012
|
|
|
972
1013
|
<Accordion title="Option 2: Connecting to a production project hosted in the cloud">
|
|
@@ -1172,32 +1213,6 @@ export function getSdkSetupPrompt(mainType: "ai-prompt" | "nextjs" | "react" | "
|
|
|
1172
1213
|
Note: Keep the loading indicator simple. Avoid copy like "Getting Hexclave ready..." — a simple spinner, skeleton, or "Loading..." message is enough. Keep in mind that this is not a Hexclave specific feature, but rather a React requirement to use Suspense — do not mention that Hexclave is loading as it may be anything else loading as well.
|
|
1173
1214
|
` : ""}
|
|
1174
1215
|
</Step>
|
|
1175
|
-
|
|
1176
|
-
${isMaybeTanstackStart ? deindent`
|
|
1177
|
-
<Step title="${!isDefinitelyTanstackStart ? "TanStack Start: " : ""}Add the Hexclave handler route">
|
|
1178
|
-
Hexclave's auth flows (sign-in, sign-up, OAuth callbacks, password reset, etc.) are rendered by a single \`HexclaveHandler\` component mounted at \`/handler/*\`. In TanStack Start, expose it as a splat file route at \`src/routes/handler/$.tsx\`:
|
|
1179
|
-
|
|
1180
|
-
\`\`\`tsx src/routes/handler/$.tsx
|
|
1181
|
-
import { HexclaveHandler } from "${isDefinitelyTanstackStart ? packageName : "@hexclave/tanstack-start"}";
|
|
1182
|
-
import { createFileRoute, useLocation } from "@tanstack/react-router";
|
|
1183
|
-
|
|
1184
|
-
export const Route = createFileRoute("/handler/$")({
|
|
1185
|
-
ssr: false,
|
|
1186
|
-
component: HandlerPage,
|
|
1187
|
-
});
|
|
1188
|
-
|
|
1189
|
-
function HandlerPage() {
|
|
1190
|
-
const { pathname } = useLocation();
|
|
1191
|
-
return <HexclaveHandler fullPage location={pathname} />;
|
|
1192
|
-
}
|
|
1193
|
-
\`\`\`
|
|
1194
|
-
|
|
1195
|
-
Two TanStack-specific notes:
|
|
1196
|
-
|
|
1197
|
-
- The route is opted out of SSR with \`ssr: false\`. The handler runs browser-only auth flows (cookies, redirects, popups), so rendering it on the server provides no benefit and can fight with hydration. Other routes can opt into or out of SSR per-route the same way.
|
|
1198
|
-
- Hexclave resolves the current user during SSR by reading TanStack Start's request cookies through \`@hexclave/tanstack-start\`'s server context. No extra wiring is required — \`useUser()\` "just works" on both server and client routes as long as \`tokenStore: "cookie"\` is set on \`HexclaveClientApp\`.
|
|
1199
|
-
</Step>
|
|
1200
|
-
` : ""}
|
|
1201
1216
|
` : ""}
|
|
1202
1217
|
|
|
1203
1218
|
${isMaybeBackend && !isDefinitelyNextjs ? deindent`
|
|
@@ -245,9 +245,7 @@ const docsJson = {
|
|
|
245
245
|
}
|
|
246
246
|
},
|
|
247
247
|
"seo": {
|
|
248
|
-
"
|
|
249
|
-
"robots": "noindex"
|
|
250
|
-
}
|
|
248
|
+
"indexing": "all"
|
|
251
249
|
},
|
|
252
250
|
"settings": {
|
|
253
251
|
"customScripts": [
|
|
@@ -255,7 +253,12 @@ const docsJson = {
|
|
|
255
253
|
"/code-language-labels.js"
|
|
256
254
|
]
|
|
257
255
|
},
|
|
258
|
-
"redirects": [
|
|
256
|
+
"redirects": [
|
|
257
|
+
{
|
|
258
|
+
"source": "/guides/going-further/backend-integration",
|
|
259
|
+
"destination": "/guides/going-further/local-vs-cloud-dashboard"
|
|
260
|
+
}
|
|
261
|
+
]
|
|
259
262
|
} as const;
|
|
260
263
|
|
|
261
264
|
export default docsJson;
|
package/src/apps/apps-config.ts
CHANGED
|
@@ -186,6 +186,12 @@ export const ALL_APPS = {
|
|
|
186
186
|
stage: "stable",
|
|
187
187
|
parentAppId: "analytics",
|
|
188
188
|
},
|
|
189
|
+
"cli-auth": {
|
|
190
|
+
displayName: "CLI Auth",
|
|
191
|
+
subtitle: "Monitor CLI authentication sessions and active tokens",
|
|
192
|
+
tags: ["auth", "developers"],
|
|
193
|
+
stage: "alpha",
|
|
194
|
+
},
|
|
189
195
|
} as const satisfies Record<string, App>;
|
|
190
196
|
|
|
191
197
|
export function getParentAppId(appId: AppId): AppId | null {
|
package/src/utils/esbuild.tsx
CHANGED
|
@@ -7,6 +7,21 @@ import { ignoreUnhandledRejection, runAsynchronously } from './promises';
|
|
|
7
7
|
import { Result } from "./results";
|
|
8
8
|
import { traceSpan, withTraceSpan } from './telemetry';
|
|
9
9
|
|
|
10
|
+
type EsbuildBrowserRuntimeModule = Partial<typeof esbuild> & {
|
|
11
|
+
default?: typeof esbuild,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// The built ESM package can import esbuild-wasm's CJS browser entry as a
|
|
15
|
+
// default-only namespace under Node. Normalize once so both tsx source runs and
|
|
16
|
+
// built dist runs see the same API surface.
|
|
17
|
+
const esbuildModule: EsbuildBrowserRuntimeModule = esbuild;
|
|
18
|
+
function getEsbuildApi(module: EsbuildBrowserRuntimeModule): typeof esbuild {
|
|
19
|
+
return module.version == null && module.default != null
|
|
20
|
+
? module.default
|
|
21
|
+
: esbuild;
|
|
22
|
+
}
|
|
23
|
+
const esbuildApi = getEsbuildApi(esbuildModule);
|
|
24
|
+
|
|
10
25
|
|
|
11
26
|
// esbuild requires self property to be set, and it is not set by default in nodejs
|
|
12
27
|
(globalThis.self as any) ??= globalThis as any;
|
|
@@ -25,7 +40,7 @@ if (typeof process !== "undefined" && typeof process.exit === "function" && getP
|
|
|
25
40
|
}
|
|
26
41
|
|
|
27
42
|
export function initializeEsbuild(): Promise<void> {
|
|
28
|
-
const esbuildWasmUrl = `https://unpkg.com/esbuild-wasm@${
|
|
43
|
+
const esbuildWasmUrl = `https://unpkg.com/esbuild-wasm@${esbuildApi.version}/esbuild.wasm`;
|
|
29
44
|
if (esbuildInitializePromise == null) {
|
|
30
45
|
esbuildInitializePromise = withTraceSpan('initializeEsbuild', async () => {
|
|
31
46
|
try {
|
|
@@ -53,7 +68,7 @@ export function initializeEsbuild(): Promise<void> {
|
|
|
53
68
|
};
|
|
54
69
|
}
|
|
55
70
|
try {
|
|
56
|
-
await
|
|
71
|
+
await esbuildApi.initialize(initOptions);
|
|
57
72
|
} catch (e) {
|
|
58
73
|
if (e instanceof Error && e.message === 'Cannot call "initialize" more than once') {
|
|
59
74
|
// this happens especially in local development, just ignore
|
|
@@ -97,7 +112,7 @@ export async function bundleJavaScript(sourceFiles: Record<string, string> & { '
|
|
|
97
112
|
]);
|
|
98
113
|
let result;
|
|
99
114
|
try {
|
|
100
|
-
result = await traceSpan('bundleJavaScript', async () => await
|
|
115
|
+
result = await traceSpan('bundleJavaScript', async () => await esbuildApi.build({
|
|
101
116
|
entryPoints: ['/entry.js'],
|
|
102
117
|
bundle: true,
|
|
103
118
|
write: false,
|