@hitch42/applet 0.1.1-beta.14 → 0.1.1-beta.16
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/_internal/server.d.ts +10 -1
- package/dist/_internal/server.js +16 -2
- package/dist/applet.js +12 -12
- package/dist/auth-D3CrCtZq.js +21 -0
- package/dist/{build-CwOM9C0W.js → build-XHoa_sJL.js} +1 -1
- package/dist/build-ueR7XjZT.js +2 -0
- package/dist/{ci-BKw14X2h.js → ci-B5-mH-YW.js} +1 -1
- package/dist/client.d.ts +3 -3
- package/dist/contract-BbOj5EB7.js +23 -0
- package/dist/{contract-CZdT22tB.d.ts → contract-D9wEvxGI.d.ts} +1 -1
- package/dist/{deploy-CaGPhe2h.js → deploy-C5JDIn2Y.js} +4 -3
- package/dist/device-BgIfytaR.js +135 -0
- package/dist/email-protocol-B9u_dh2p.d.ts +202 -0
- package/dist/{generate-CORu1X2X.js → generate-BV_emZgy.js} +39 -13
- package/dist/generate-C9bWqdaB.js +2 -0
- package/dist/hitch-file-url-BPKj9zVG.js +25 -0
- package/dist/index.d.ts +15 -25
- package/dist/index.js +80 -192
- package/dist/{init-CD-xOYWV.js → init-BGQ_bP44.js} +5 -5
- package/dist/{manifest-CuY3ICpN.js → load-config-Dkr2VxZN.js} +37 -2
- package/dist/{login-CUx3TmFQ.js → login-CceGvlMi.js} +3 -1
- package/dist/{logs-BySh9fnH.js → logs-DOkT5ots.js} +3 -3
- package/dist/{manifest-ARAy2HP7.d.ts → manifest-BSUtHbu-.d.ts} +1 -4
- package/dist/markdown-CwlqzBtP.js +88 -0
- package/dist/markdown-DSHOQIOb.d.ts +14 -0
- package/dist/markdown-editor-BmEaBzd7.d.ts +27 -0
- package/dist/markdown-editor-C5mLHGqu.js +428 -0
- package/dist/{project-z7x0omdw.js → project-CCVqCCgU.js} +5 -4
- package/dist/react/markdown-editor.d.ts +2 -0
- package/dist/react/markdown-editor.js +2 -0
- package/dist/react/markdown.d.ts +2 -0
- package/dist/react/markdown.js +2 -0
- package/dist/react.d.ts +2 -37
- package/dist/react.js +2 -534
- package/dist/{schedules-B_p9U-vM.js → schedules-D6-3v-Az.js} +1 -1
- package/dist/server.d.ts +3 -3
- package/dist/store-B8N_8JIN.js +196 -0
- package/dist/{upload-DKZzz5kW.d.ts → upload-D_kkmne_.d.ts} +5 -9
- package/dist/{variables-D9nEFyOK.js → variables-BSFZnLxY.js} +2 -2
- package/dist/vite.d.ts +1 -1
- package/dist/vite.js +165 -35
- package/dist/workspace-8yqXsvce.js +31 -0
- package/fonts/inter/LICENSE.txt +92 -0
- package/fonts/inter/inter-cyrillic-ext.woff2 +0 -0
- package/fonts/inter/inter-cyrillic.woff2 +0 -0
- package/fonts/inter/inter-greek-ext.woff2 +0 -0
- package/fonts/inter/inter-greek.woff2 +0 -0
- package/fonts/inter/inter-latin-ext.woff2 +0 -0
- package/fonts/inter/inter-latin.woff2 +0 -0
- package/fonts/inter/inter-vietnamese.woff2 +0 -0
- package/fonts/inter.css +81 -0
- package/package.json +15 -1
- package/scaffold/.agents/skills/hitch-applets/references/markdown.md +20 -2
- package/scaffold/applet/src/client/styles/globals.css +6 -1
- package/dist/build-CeRZYhM9.js +0 -2
- package/dist/email-WA4jnzZ0.js +0 -211
- package/dist/email-protocol-DqJ6YCiC.d.ts +0 -230
- package/dist/generate-CtbF9Zxd.js +0 -2
- package/dist/load-config-BeQZ2yOa.js +0 -27
- package/dist/me-BW7Paary.js +0 -32
- package/dist/workspace-D0dnnlxL.js +0 -385
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
//#region ../shared/auth/src/store.mts
|
|
5
|
+
const DEFAULT_INSTANCE_URL = "https://app.hitch42.com";
|
|
6
|
+
var AuthenticationError = class extends Error {};
|
|
7
|
+
function isErrno(error, code) {
|
|
8
|
+
return error instanceof Error && "code" in error && error.code === code;
|
|
9
|
+
}
|
|
10
|
+
async function readJson(path) {
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(await readFile(path, "utf8"));
|
|
13
|
+
} catch (error) {
|
|
14
|
+
if (isErrno(error, "ENOENT")) return void 0;
|
|
15
|
+
throw new Error(`Failed to read ${path}.`, { cause: error });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function isObject(value) {
|
|
19
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
20
|
+
}
|
|
21
|
+
function emptyCredentialStore() {
|
|
22
|
+
return { instances: {} };
|
|
23
|
+
}
|
|
24
|
+
async function healCredentialStore(credentialsPath, source, store, unrecognizedCount) {
|
|
25
|
+
const backupPath = `${credentialsPath}.bak`;
|
|
26
|
+
try {
|
|
27
|
+
try {
|
|
28
|
+
await writeFile(backupPath, source, {
|
|
29
|
+
flag: "wx",
|
|
30
|
+
mode: 384
|
|
31
|
+
});
|
|
32
|
+
} catch (error) {
|
|
33
|
+
if (!isErrno(error, "EEXIST")) throw error;
|
|
34
|
+
}
|
|
35
|
+
await chmod(backupPath, 384);
|
|
36
|
+
await writeFile(credentialsPath, `${JSON.stringify(store, null, 2)}\n`, { mode: 384 });
|
|
37
|
+
await chmod(credentialsPath, 384);
|
|
38
|
+
} catch {
|
|
39
|
+
console.error(unrecognizedCount === void 0 ? `hitch: failed to heal credential store at ${credentialsPath}; using an empty in-memory store.` : `hitch: failed to heal credential store at ${credentialsPath}; using recognized credentials in memory only.`);
|
|
40
|
+
return store;
|
|
41
|
+
}
|
|
42
|
+
console.error(unrecognizedCount === void 0 ? `hitch: reset unreadable credentials file (backup: ${backupPath}).` : unrecognizedCount === 0 ? `hitch: migrated credentials to per-organization storage (backup: ${backupPath}).` : `hitch: removed ${unrecognizedCount} unrecognized credential ${unrecognizedCount === 1 ? "entry" : "entries"} (backup: ${backupPath}).`);
|
|
43
|
+
return store;
|
|
44
|
+
}
|
|
45
|
+
async function readRawCredentialStore() {
|
|
46
|
+
const { credentialsPath } = storePaths();
|
|
47
|
+
let source;
|
|
48
|
+
try {
|
|
49
|
+
source = await readFile(credentialsPath, "utf8");
|
|
50
|
+
} catch (error) {
|
|
51
|
+
if (isErrno(error, "ENOENT")) return emptyCredentialStore();
|
|
52
|
+
throw new Error(`Failed to read ${credentialsPath}.`, { cause: error });
|
|
53
|
+
}
|
|
54
|
+
let value;
|
|
55
|
+
try {
|
|
56
|
+
value = JSON.parse(source);
|
|
57
|
+
} catch {
|
|
58
|
+
return healCredentialStore(credentialsPath, source, emptyCredentialStore());
|
|
59
|
+
}
|
|
60
|
+
if (!isObject(value) || !isObject(value.instances)) return healCredentialStore(credentialsPath, source, emptyCredentialStore());
|
|
61
|
+
const instances = Object.fromEntries(Object.entries(value.instances).flatMap(([instanceUrl, entry]) => {
|
|
62
|
+
if (isObject(entry) && typeof entry.organizationId === "string") {
|
|
63
|
+
const credentials = parseCredentials(entry);
|
|
64
|
+
return credentials === void 0 ? [] : [[instanceUrl, { [credentials.orgCode]: entry }]];
|
|
65
|
+
}
|
|
66
|
+
if (!isObject(entry)) return [];
|
|
67
|
+
return [[instanceUrl, Object.fromEntries(Object.entries(entry).filter(([orgCode, credentials]) => parseCredentials(credentials)?.orgCode === orgCode))]];
|
|
68
|
+
}));
|
|
69
|
+
const reshaped = Object.values(value.instances).some((entry) => !isObject(entry) || typeof entry.organizationId === "string");
|
|
70
|
+
const countLeaves = (store) => Object.values(store).reduce((total, entry) => total + (isObject(entry) && typeof entry.organizationId !== "string" ? Object.keys(entry).length : 1), 0);
|
|
71
|
+
const unrecognizedCount = countLeaves(value.instances) - countLeaves(instances);
|
|
72
|
+
if (!reshaped && unrecognizedCount === 0) return value;
|
|
73
|
+
return healCredentialStore(credentialsPath, source, { instances }, unrecognizedCount);
|
|
74
|
+
}
|
|
75
|
+
function parseCredentials(value) {
|
|
76
|
+
if (!isObject(value) || typeof value.organizationId !== "string" || typeof value.orgCode !== "string") return;
|
|
77
|
+
const organization = {
|
|
78
|
+
organizationId: value.organizationId,
|
|
79
|
+
orgCode: value.orgCode
|
|
80
|
+
};
|
|
81
|
+
if ((value.type === void 0 || value.type === "session") && typeof value.accessToken === "string" && (value.expiresAt === void 0 || typeof value.expiresAt === "number")) return {
|
|
82
|
+
type: "session",
|
|
83
|
+
accessToken: value.accessToken,
|
|
84
|
+
...value.expiresAt === void 0 ? {} : { expiresAt: value.expiresAt },
|
|
85
|
+
...organization
|
|
86
|
+
};
|
|
87
|
+
if (value.type === "apiKey" && typeof value.apiKey === "string") return {
|
|
88
|
+
type: "apiKey",
|
|
89
|
+
apiKey: value.apiKey,
|
|
90
|
+
...organization
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function normalizeInstanceUrl(instanceUrl) {
|
|
94
|
+
let url;
|
|
95
|
+
try {
|
|
96
|
+
url = new URL(instanceUrl);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
throw new Error(`Invalid Hitch instance URL: ${instanceUrl}`, { cause: error });
|
|
99
|
+
}
|
|
100
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") throw new Error(`Invalid Hitch instance URL protocol: ${url.protocol}`);
|
|
101
|
+
url.pathname = url.pathname.replace(/\/+$/, "");
|
|
102
|
+
url.search = "";
|
|
103
|
+
url.hash = "";
|
|
104
|
+
return url.toString().replace(/\/$/, "");
|
|
105
|
+
}
|
|
106
|
+
function organizationInstanceUrl(instanceUrl, orgCode) {
|
|
107
|
+
const url = new URL(normalizeInstanceUrl(instanceUrl));
|
|
108
|
+
const labels = url.hostname.split(".");
|
|
109
|
+
if (labels[0] !== "app" || labels.length < 2) return url.toString().replace(/\/$/, "");
|
|
110
|
+
url.hostname = [orgCode, ...labels.slice(1)].join(".");
|
|
111
|
+
return url.toString().replace(/\/$/, "");
|
|
112
|
+
}
|
|
113
|
+
function storePaths() {
|
|
114
|
+
const env = process.env;
|
|
115
|
+
const home = homedir();
|
|
116
|
+
const configDir = join(env.XDG_CONFIG_HOME || join(home, ".config"), "hitch");
|
|
117
|
+
return {
|
|
118
|
+
configDir,
|
|
119
|
+
cacheDir: join(env.XDG_CACHE_HOME || join(home, ".cache"), "hitch"),
|
|
120
|
+
configPath: join(configDir, "config.json"),
|
|
121
|
+
credentialsPath: join(configDir, "credentials.json")
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
async function resolveInstanceUrl(override) {
|
|
125
|
+
if (override !== void 0) return normalizeInstanceUrl(override);
|
|
126
|
+
const envInstanceUrl = process.env.HITCH_API_URL;
|
|
127
|
+
if (envInstanceUrl !== void 0 && envInstanceUrl.length > 0) return normalizeInstanceUrl(envInstanceUrl);
|
|
128
|
+
const { configPath } = storePaths();
|
|
129
|
+
const config = await readJson(configPath);
|
|
130
|
+
if (config === void 0) return DEFAULT_INSTANCE_URL;
|
|
131
|
+
if (!isObject(config) || config.instanceUrl !== void 0 && typeof config.instanceUrl !== "string") throw new Error(`${configPath}: expected { instanceUrl?: string }.`);
|
|
132
|
+
return normalizeInstanceUrl(config.instanceUrl ?? "https://app.hitch42.com");
|
|
133
|
+
}
|
|
134
|
+
async function readCredentialStore() {
|
|
135
|
+
const store = await readRawCredentialStore();
|
|
136
|
+
return { instances: Object.fromEntries(Object.entries(store.instances).map(([instanceUrl, credentialsByOrg]) => [instanceUrl, Object.fromEntries(Object.entries(credentialsByOrg).flatMap(([orgCode, value]) => {
|
|
137
|
+
const credentials = parseCredentials(value);
|
|
138
|
+
return credentials === void 0 ? [] : [[orgCode, credentials]];
|
|
139
|
+
}))])) };
|
|
140
|
+
}
|
|
141
|
+
async function getCredentialsForOrg(instanceUrl, orgCode) {
|
|
142
|
+
return (await readCredentialStore()).instances[normalizeInstanceUrl(instanceUrl)]?.[orgCode];
|
|
143
|
+
}
|
|
144
|
+
function environmentAuth() {
|
|
145
|
+
const apiKey = process.env.HITCH_API_KEY;
|
|
146
|
+
return apiKey !== void 0 && apiKey.length > 0 ? {
|
|
147
|
+
accessToken: apiKey,
|
|
148
|
+
source: "environment"
|
|
149
|
+
} : void 0;
|
|
150
|
+
}
|
|
151
|
+
function resolvedStoredAuth(credentials) {
|
|
152
|
+
return credentials === void 0 ? void 0 : {
|
|
153
|
+
accessToken: credentials.type === "apiKey" ? credentials.apiKey : credentials.accessToken,
|
|
154
|
+
credentialType: credentials.type,
|
|
155
|
+
organizationId: credentials.organizationId,
|
|
156
|
+
orgCode: credentials.orgCode,
|
|
157
|
+
source: "store"
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function resolveStoredAuthForOrg(credentialsByOrg, orgCode) {
|
|
161
|
+
const exact = resolvedStoredAuth(credentialsByOrg[orgCode]);
|
|
162
|
+
if (exact !== void 0) return exact;
|
|
163
|
+
const sessions = Object.values(credentialsByOrg).filter((credentials) => credentials.type === "session").filter((credentials, index, all) => all.findIndex(({ accessToken }) => accessToken === credentials.accessToken) === index);
|
|
164
|
+
if (sessions.length === 1) return resolvedStoredAuth(sessions[0]);
|
|
165
|
+
if (sessions.length > 1) throw new AuthenticationError(`Multiple sessions are stored. Run hitch login for organization "${orgCode}".`);
|
|
166
|
+
const apiKeyOrgs = Object.values(credentialsByOrg).filter((credentials) => credentials.type === "apiKey").map(({ orgCode: storedOrg }) => storedOrg).sort();
|
|
167
|
+
if (apiKeyOrgs.length > 0) throw new AuthenticationError(`Stored API ${apiKeyOrgs.length === 1 ? "key is" : "keys are"} scoped to ${apiKeyOrgs.map((storedOrg) => `"${storedOrg}"`).join(", ")}, not "${orgCode}".`);
|
|
168
|
+
}
|
|
169
|
+
async function resolveAuthForOrg(instanceUrl, orgCode) {
|
|
170
|
+
const auth = environmentAuth();
|
|
171
|
+
if (auth !== void 0) return auth;
|
|
172
|
+
const normalizedUrl = normalizeInstanceUrl(instanceUrl);
|
|
173
|
+
const credentialsByOrg = Object.entries((await readCredentialStore()).instances).find(([instance]) => instance === normalizedUrl)?.[1];
|
|
174
|
+
return resolveStoredAuthForOrg(credentialsByOrg ?? {}, orgCode);
|
|
175
|
+
}
|
|
176
|
+
function rejectEnvironmentKey(response, auth) {
|
|
177
|
+
if (auth.source === "environment" && response.status === 401) throw new AuthenticationError("HITCH_API_KEY was rejected.");
|
|
178
|
+
}
|
|
179
|
+
function bearerToken(credentials) {
|
|
180
|
+
return "apiKey" in credentials ? credentials.apiKey : credentials.accessToken;
|
|
181
|
+
}
|
|
182
|
+
async function setCredentials(instanceUrl, credentials) {
|
|
183
|
+
const paths = storePaths();
|
|
184
|
+
const store = await readRawCredentialStore();
|
|
185
|
+
const normalizedUrl = normalizeInstanceUrl(instanceUrl);
|
|
186
|
+
const credentialsByOrg = store.instances[normalizedUrl];
|
|
187
|
+
store.instances[normalizedUrl] = {
|
|
188
|
+
...isObject(credentialsByOrg) ? credentialsByOrg : {},
|
|
189
|
+
[credentials.orgCode]: credentials
|
|
190
|
+
};
|
|
191
|
+
await mkdir(paths.configDir, { recursive: true });
|
|
192
|
+
await writeFile(paths.credentialsPath, `${JSON.stringify(store, null, 2)}\n`, { mode: 384 });
|
|
193
|
+
await chmod(paths.credentialsPath, 384);
|
|
194
|
+
}
|
|
195
|
+
//#endregion
|
|
196
|
+
export { organizationInstanceUrl as a, resolveInstanceUrl as c, getCredentialsForOrg as i, setCredentials as l, DEFAULT_INSTANCE_URL as n, rejectEnvironmentKey as o, bearerToken as r, resolveAuthForOrg as s, AuthenticationError as t };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { ContractProcedure, Meta } from "@orpc/contract";
|
|
2
|
-
import { z } from "zod";
|
|
1
|
+
import { ContractProcedure, Meta, Schema } from "@orpc/contract";
|
|
3
2
|
//#region src/hitch/files.d.ts
|
|
4
3
|
type HitchFile = {
|
|
5
4
|
id: string;
|
|
@@ -36,13 +35,10 @@ type FileOutput = {
|
|
|
36
35
|
data: HitchFile;
|
|
37
36
|
downloadUrl: string;
|
|
38
37
|
};
|
|
39
|
-
declare const fileSchema: z.ZodType<HitchFile, HitchFile>;
|
|
40
|
-
declare const createFileOutputSchema: z.ZodType<CreateFileOutput, CreateFileOutput>;
|
|
41
|
-
declare const getFileOutputSchema: z.ZodType<FileOutput, FileOutput>;
|
|
42
38
|
type FilesContract = {
|
|
43
|
-
create: ContractProcedure<
|
|
44
|
-
confirm: ContractProcedure<
|
|
45
|
-
get: ContractProcedure<
|
|
39
|
+
create: ContractProcedure<Schema<CreateFileInput, CreateFileInput>, Schema<CreateFileOutput, CreateFileOutput>, Record<never, never>, Meta>;
|
|
40
|
+
confirm: ContractProcedure<Schema<FileIdInput, FileIdInput>, Schema<FileOutput, FileOutput>, Record<never, never>, Meta>;
|
|
41
|
+
get: ContractProcedure<Schema<FileIdInput, FileIdInput>, Schema<FileOutput, FileOutput>, Record<never, never>, Meta>;
|
|
46
42
|
};
|
|
47
43
|
declare const filesContract: FilesContract;
|
|
48
44
|
//#endregion
|
|
@@ -109,4 +105,4 @@ type WithFilesUpload<T> = T extends {
|
|
|
109
105
|
files: FilesUpload;
|
|
110
106
|
};
|
|
111
107
|
//#endregion
|
|
112
|
-
export { UploadedHitchFile as a,
|
|
108
|
+
export { UploadedHitchFile as a, filesContract as c, UploadProgress as i, FilesUpload as n, WithFilesUpload as o, UploadFileOptions as r, uploadFile as s, FilesClient as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as loadAppletConfig } from "./load-config-
|
|
1
|
+
import { t as fetchWithOrgAuth } from "./auth-D3CrCtZq.js";
|
|
2
|
+
import { t as loadAppletConfig } from "./load-config-Dkr2VxZN.js";
|
|
3
3
|
import { t as question } from "./prompt-ChS-grYO.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
//#region src/variables.ts
|
package/dist/vite.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as RegisteredAppletSourceDefinition } from "./manifest-
|
|
1
|
+
import { o as RegisteredAppletSourceDefinition } from "./manifest-BSUtHbu-.js";
|
|
2
2
|
import { PluginOption } from "vite";
|
|
3
3
|
//#region src/vite.d.ts
|
|
4
4
|
declare function applet<const M extends RegisteredAppletSourceDefinition>(sourceDefinition: M): PluginOption[];
|
package/dist/vite.js
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as APPLET_CODE_PATTERN, n as appletManifestSchema, r as appletSourceDefinitionSchema } from "./manifest-CuY3ICpN.js";
|
|
1
|
+
import { t as pruneContract } from "./contract-BbOj5EB7.js";
|
|
3
2
|
import { a as openQueryCacheDatabase } from "./query-cache-CXmAyLv3.js";
|
|
4
3
|
import { d as HITCH_USER_HEADER, u as HITCH_SESSION_HEADER } from "./headers-DkNr9MLJ.js";
|
|
5
4
|
import { a as workflowOutcomeSchema, n as runWorkflowJournal } from "./workflow-engine-BOTzURKM.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { a as organizationInstanceUrl, c as resolveInstanceUrl, o as rejectEnvironmentKey, r as bearerToken, s as resolveAuthForOrg } from "./store-B8N_8JIN.js";
|
|
6
|
+
import { a as writeRegisterDeclaration, c as writeAppletRoot, d as writeServerEntry, i as syncContract, l as writeClientEntry, n as routesModuleSource, u as writeManifestGen } from "./generate-BV_emZgy.js";
|
|
7
|
+
import { a as appletSourceDefinitionSchema, i as appletManifestSchema, o as APPLET_CODE_PATTERN, r as resolveWorkspaceOrg } from "./load-config-Dkr2VxZN.js";
|
|
8
|
+
import { z } from "zod";
|
|
8
9
|
import { randomUUID } from "node:crypto";
|
|
9
10
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
10
11
|
import { appendFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
11
12
|
import { createServer } from "node:http";
|
|
12
13
|
import { basename, dirname, relative, resolve } from "node:path";
|
|
14
|
+
import { pathToFileURL } from "node:url";
|
|
13
15
|
import { cloudflare } from "@cloudflare/vite-plugin";
|
|
16
|
+
import optimizeLocales from "@react-aria/optimize-locales-plugin";
|
|
14
17
|
import { tanstackRouter } from "@tanstack/router-plugin/vite";
|
|
15
18
|
import react from "@vitejs/plugin-react";
|
|
19
|
+
import { normalizePath } from "vite";
|
|
16
20
|
import { Readable } from "node:stream";
|
|
17
21
|
import { pipeline } from "node:stream/promises";
|
|
18
22
|
//#region src/dev-proxy.ts
|
|
@@ -292,6 +296,31 @@ function createDevWorkflows(options) {
|
|
|
292
296
|
};
|
|
293
297
|
}
|
|
294
298
|
//#endregion
|
|
299
|
+
//#region src/hitch/me-schemas.ts
|
|
300
|
+
const personSchema = z.object({
|
|
301
|
+
id: z.uuid(),
|
|
302
|
+
firstName: z.string(),
|
|
303
|
+
lastName: z.string(),
|
|
304
|
+
email: z.email()
|
|
305
|
+
});
|
|
306
|
+
const membershipSchema = z.object({
|
|
307
|
+
orgId: z.uuid(),
|
|
308
|
+
code: z.string(),
|
|
309
|
+
name: z.string(),
|
|
310
|
+
iconFileId: z.uuid().nullable(),
|
|
311
|
+
role: z.enum(["member", "owner"]),
|
|
312
|
+
capabilities: z.array(z.string())
|
|
313
|
+
});
|
|
314
|
+
const meSchema = z.object({
|
|
315
|
+
person: personSchema,
|
|
316
|
+
memberships: z.array(membershipSchema),
|
|
317
|
+
organization: z.object({
|
|
318
|
+
id: z.uuid(),
|
|
319
|
+
code: z.string()
|
|
320
|
+
}).nullable(),
|
|
321
|
+
multiOrg: z.boolean()
|
|
322
|
+
});
|
|
323
|
+
//#endregion
|
|
295
324
|
//#region src/vite.ts
|
|
296
325
|
const APPLET_COMPATIBILITY_DATE = "2026-07-14";
|
|
297
326
|
const APPLET_COMPATIBILITY_FLAGS = ["nodejs_compat"];
|
|
@@ -302,12 +331,20 @@ function clientBootstrapScript(parentOrigin, appletCode, cacheQueries) {
|
|
|
302
331
|
var appletCode = ${JSON.stringify(appletCode)};
|
|
303
332
|
var appletCodePattern = ${APPLET_CODE_PATTERN.toString()};
|
|
304
333
|
var cacheQueries = ${cacheQueries} && window.parent !== window && parentOrigin !== null;
|
|
334
|
+
var cacheContextElement = document.getElementById("hitch-cache-context");
|
|
305
335
|
|
|
306
336
|
Object.defineProperty(window, "__hitchCacheContext", {
|
|
307
337
|
configurable: false,
|
|
308
338
|
writable: false,
|
|
309
339
|
value: cacheQueries
|
|
310
|
-
?
|
|
340
|
+
? cacheContextElement
|
|
341
|
+
? Promise.resolve().then(function () {
|
|
342
|
+
return JSON.parse(cacheContextElement.textContent);
|
|
343
|
+
}).catch(function (error) {
|
|
344
|
+
console.error("Could not load the applet cache context.", error);
|
|
345
|
+
return null;
|
|
346
|
+
})
|
|
347
|
+
: fetch("/__applet/context/" + encodeURIComponent(appletCode), {
|
|
311
348
|
credentials: "same-origin",
|
|
312
349
|
headers: { accept: "application/json" }
|
|
313
350
|
}).then(function (response) {
|
|
@@ -349,6 +386,52 @@ function clientBootstrapScript(parentOrigin, appletCode, cacheQueries) {
|
|
|
349
386
|
);
|
|
350
387
|
}
|
|
351
388
|
|
|
389
|
+
function prefetchAppletAssets(code) {
|
|
390
|
+
var key = "hitch:prefetched:" + code;
|
|
391
|
+
function clearPrefetchRecord() {
|
|
392
|
+
try {
|
|
393
|
+
window.sessionStorage.removeItem(key);
|
|
394
|
+
} catch (error) {
|
|
395
|
+
console.warn("Could not clear the applet prefetch record.", error);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
try {
|
|
399
|
+
var lastPrefetch = Number(window.sessionStorage.getItem(key));
|
|
400
|
+
if (lastPrefetch && Date.now() - lastPrefetch < 10 * 60 * 1000) return;
|
|
401
|
+
window.sessionStorage.setItem(key, String(Date.now()));
|
|
402
|
+
} catch (error) {
|
|
403
|
+
console.warn("Could not record the applet prefetch.", error);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
fetch("/" + encodeURIComponent(code), {
|
|
407
|
+
credentials: "same-origin",
|
|
408
|
+
headers: { accept: "text/html" }
|
|
409
|
+
}).then(function (response) {
|
|
410
|
+
if (!response.ok) {
|
|
411
|
+
clearPrefetchRecord();
|
|
412
|
+
console.warn("Could not prefetch the applet document: " + response.status);
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
return response.text();
|
|
416
|
+
}).then(function (html) {
|
|
417
|
+
if (html === null) return;
|
|
418
|
+
var parsed = new DOMParser().parseFromString(html, "text/html");
|
|
419
|
+
parsed.querySelectorAll('script[src], link[rel="stylesheet"][href], link[rel="modulepreload"][href]').forEach(function (element) {
|
|
420
|
+
var source = element.getAttribute(element.tagName === "SCRIPT" ? "src" : "href");
|
|
421
|
+
var url = new URL(source, window.location.origin);
|
|
422
|
+
if (url.origin !== window.location.origin || !url.pathname.startsWith("/" + code + "/assets/")) return;
|
|
423
|
+
var link = document.createElement("link");
|
|
424
|
+
link.rel = "prefetch";
|
|
425
|
+
link.href = url.href;
|
|
426
|
+
if (element.crossOrigin !== null) link.crossOrigin = element.crossOrigin;
|
|
427
|
+
document.head.appendChild(link);
|
|
428
|
+
});
|
|
429
|
+
}).catch(function (error) {
|
|
430
|
+
clearPrefetchRecord();
|
|
431
|
+
console.warn("Could not prefetch the applet assets.", error);
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
|
|
352
435
|
var params = new URLSearchParams(window.location.search);
|
|
353
436
|
var theme = params.get("_hitch_theme");
|
|
354
437
|
var dark = theme === "dark" ||
|
|
@@ -375,66 +458,96 @@ function clientBootstrapScript(parentOrigin, appletCode, cacheQueries) {
|
|
|
375
458
|
window.addEventListener("popstate", reportLocation);
|
|
376
459
|
window.addEventListener("hashchange", reportLocation);
|
|
377
460
|
|
|
378
|
-
|
|
379
|
-
if (
|
|
380
|
-
event.source !== window.parent ||
|
|
381
|
-
(parentOrigin !== null && event.origin !== parentOrigin) ||
|
|
382
|
-
!event.data
|
|
383
|
-
) return;
|
|
384
|
-
|
|
461
|
+
function handleParentMessage(data) {
|
|
385
462
|
if (
|
|
386
463
|
parentOrigin !== null &&
|
|
387
|
-
|
|
464
|
+
data.type === "hitch:location-request"
|
|
388
465
|
) {
|
|
389
466
|
reportLocation();
|
|
390
467
|
return;
|
|
391
468
|
}
|
|
392
469
|
|
|
393
470
|
if (
|
|
394
|
-
|
|
395
|
-
(
|
|
471
|
+
data.type === "hitch:theme" &&
|
|
472
|
+
(data.theme === "light" || data.theme === "dark")
|
|
396
473
|
) {
|
|
397
|
-
applyTheme(
|
|
474
|
+
applyTheme(data.theme);
|
|
398
475
|
return;
|
|
399
476
|
}
|
|
400
477
|
|
|
401
478
|
if (
|
|
402
479
|
parentOrigin !== null &&
|
|
403
|
-
|
|
404
|
-
typeof
|
|
480
|
+
data.type === "hitch:navigation-probe" &&
|
|
481
|
+
typeof data.requestId === "string"
|
|
405
482
|
) {
|
|
406
483
|
window.parent.postMessage(
|
|
407
|
-
{ type: "hitch:navigation-ready", requestId:
|
|
484
|
+
{ type: "hitch:navigation-ready", requestId: data.requestId },
|
|
408
485
|
parentOrigin
|
|
409
486
|
);
|
|
410
487
|
return;
|
|
411
488
|
}
|
|
412
489
|
|
|
490
|
+
if (
|
|
491
|
+
parentOrigin !== null &&
|
|
492
|
+
data.type === "hitch:prefetch" &&
|
|
493
|
+
typeof data.appletCode === "string" &&
|
|
494
|
+
appletCodePattern.test(data.appletCode) &&
|
|
495
|
+
data.appletCode !== appletCode
|
|
496
|
+
) {
|
|
497
|
+
prefetchAppletAssets(data.appletCode);
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
|
|
413
501
|
if (
|
|
414
502
|
parentOrigin === null ||
|
|
415
|
-
|
|
416
|
-
typeof
|
|
417
|
-
!appletCodePattern.test(
|
|
418
|
-
(
|
|
503
|
+
data.type !== "hitch:navigate" ||
|
|
504
|
+
typeof data.appletCode !== "string" ||
|
|
505
|
+
!appletCodePattern.test(data.appletCode) ||
|
|
506
|
+
(data.theme !== "light" && data.theme !== "dark")
|
|
419
507
|
) return;
|
|
420
508
|
|
|
421
|
-
var nextHref = typeof
|
|
422
|
-
?
|
|
423
|
-
: "/" +
|
|
509
|
+
var nextHref = typeof data.href === "string"
|
|
510
|
+
? data.href
|
|
511
|
+
: "/" + data.appletCode;
|
|
424
512
|
var nextUrl = new URL(nextHref, window.location.origin);
|
|
425
|
-
var nextAppletPath = "/" +
|
|
513
|
+
var nextAppletPath = "/" + data.appletCode;
|
|
426
514
|
if (
|
|
427
515
|
nextUrl.origin !== window.location.origin ||
|
|
428
516
|
(nextUrl.pathname !== nextAppletPath && !nextUrl.pathname.startsWith(nextAppletPath + "/"))
|
|
429
517
|
) return;
|
|
430
518
|
|
|
431
|
-
applyTheme(
|
|
519
|
+
applyTheme(data.theme);
|
|
432
520
|
if (
|
|
433
521
|
nextUrl.pathname + nextUrl.search + nextUrl.hash ===
|
|
434
522
|
window.location.pathname + window.location.search + window.location.hash
|
|
435
523
|
) return;
|
|
436
|
-
nextUrl.searchParams.set("_hitch_theme",
|
|
524
|
+
nextUrl.searchParams.set("_hitch_theme", data.theme);
|
|
437
525
|
window.location.replace(nextUrl);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
window.addEventListener("message", function (event) {
|
|
529
|
+
if (
|
|
530
|
+
event.source !== window.parent ||
|
|
531
|
+
(parentOrigin !== null && event.origin !== parentOrigin) ||
|
|
532
|
+
!event.data
|
|
533
|
+
) return;
|
|
534
|
+
|
|
535
|
+
// A port message arrives at once; a window message waits for the
|
|
536
|
+
// dashboard's current task, which is often the render of a navigation.
|
|
537
|
+
if (parentOrigin !== null && event.data.type === "hitch:connect" && event.ports[0]) {
|
|
538
|
+
var port = event.ports[0];
|
|
539
|
+
// The port dies with this document, so tell the dashboard.
|
|
540
|
+
window.addEventListener("pagehide", function () {
|
|
541
|
+
port.postMessage({ type: "hitch:disconnect" });
|
|
542
|
+
});
|
|
543
|
+
port.onmessage = function (portEvent) {
|
|
544
|
+
if (portEvent.data) handleParentMessage(portEvent.data);
|
|
545
|
+
};
|
|
546
|
+
port.postMessage({ type: "hitch:navigation-ready" });
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
handleParentMessage(event.data);
|
|
438
551
|
});
|
|
439
552
|
})();`;
|
|
440
553
|
}
|
|
@@ -451,6 +564,7 @@ function applet(sourceDefinition) {
|
|
|
451
564
|
let buildRootOutDir;
|
|
452
565
|
let clearedOutDir = false;
|
|
453
566
|
let contractSync;
|
|
567
|
+
let buildRoutes = false;
|
|
454
568
|
let themeStylesheetUrl;
|
|
455
569
|
let devApiUrl;
|
|
456
570
|
let devListener;
|
|
@@ -521,6 +635,7 @@ function applet(sourceDefinition) {
|
|
|
521
635
|
return apiManifest;
|
|
522
636
|
} },
|
|
523
637
|
async config(config, environment) {
|
|
638
|
+
buildRoutes = environment.command === "build" && environment.mode !== "test";
|
|
524
639
|
const { root, manifest, hasClient, hasAuthorRoot, hasServer, hasRouter } = resolveAppletConfig(config.root ?? resolve("."));
|
|
525
640
|
apiManifest = manifest;
|
|
526
641
|
buildRootOutDir = resolve(root, config.build?.outDir ?? "dist");
|
|
@@ -624,6 +739,17 @@ function applet(sourceDefinition) {
|
|
|
624
739
|
configResolved(config) {
|
|
625
740
|
apiManifest = resolveAppletConfig(config.root).manifest;
|
|
626
741
|
},
|
|
742
|
+
async load(id) {
|
|
743
|
+
if (!buildRoutes) return;
|
|
744
|
+
const { root, manifest } = requireAppletConfig();
|
|
745
|
+
if (id !== normalizePath(resolve(root, ".hitch/routes.gen.ts"))) return;
|
|
746
|
+
const { contract } = await import(`${pathToFileURL(id).href}?${randomUUID()}`);
|
|
747
|
+
const { batch, ...records } = contract;
|
|
748
|
+
return routesModuleSource({
|
|
749
|
+
...pruneContract({ records }, manifest).records,
|
|
750
|
+
...Object.hasOwn(contract, "batch") ? { batch } : {}
|
|
751
|
+
});
|
|
752
|
+
},
|
|
627
753
|
configureServer(server) {
|
|
628
754
|
if (devContext === void 0) return;
|
|
629
755
|
const { manifest } = requireAppletConfig();
|
|
@@ -797,12 +923,16 @@ function applet(sourceDefinition) {
|
|
|
797
923
|
...clsxFromCn,
|
|
798
924
|
enforce: "pre"
|
|
799
925
|
},
|
|
800
|
-
...hasClientSource ? [
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
926
|
+
...hasClientSource ? [
|
|
927
|
+
tanstackRouter({
|
|
928
|
+
generatedRouteTree: ".hitch/routeTree.gen.ts",
|
|
929
|
+
routesDirectory: "src/client/routes",
|
|
930
|
+
routeFileIgnorePattern: "__root",
|
|
931
|
+
virtualRouteConfig
|
|
932
|
+
}),
|
|
933
|
+
react({ compiler: true }),
|
|
934
|
+
optimizeLocales.vite({ locales: ["en-US"] })
|
|
935
|
+
] : [],
|
|
806
936
|
cloudflarePlugins
|
|
807
937
|
];
|
|
808
938
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { basename, dirname, resolve } from "node:path";
|
|
4
|
+
//#region ../shared/auth/src/workspace.mts
|
|
5
|
+
const ORG_CODE_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
function readWorkspaceConfig(dir) {
|
|
8
|
+
const path = resolve(dir, "hitch.config.ts");
|
|
9
|
+
if (!existsSync(path)) return void 0;
|
|
10
|
+
let module;
|
|
11
|
+
try {
|
|
12
|
+
module = require(path);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
throw new Error(`Failed to load ${path}.`, { cause: error });
|
|
15
|
+
}
|
|
16
|
+
const config = module !== null && typeof module === "object" && "default" in module ? module.default : module;
|
|
17
|
+
if (config === null || typeof config !== "object" || !("sdk" in config) || typeof config.sdk !== "string" || config.sdk.length === 0 || !("org" in config) || typeof config.org !== "string" || !ORG_CODE_PATTERN.test(config.org)) throw new Error(`Invalid Hitch config in ${path}: "sdk" must be a package spec and "org" an organization code.`);
|
|
18
|
+
const githubWorkflow = "githubWorkflow" in config ? config.githubWorkflow : void 0;
|
|
19
|
+
if (githubWorkflow !== void 0 && typeof githubWorkflow !== "boolean") throw new Error(`Invalid Hitch config in ${path}: "githubWorkflow" must be a boolean.`);
|
|
20
|
+
return {
|
|
21
|
+
sdk: config.sdk,
|
|
22
|
+
org: config.org,
|
|
23
|
+
githubWorkflow
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function findWorkspaceConfig(cwd) {
|
|
27
|
+
const appletsDir = dirname(cwd);
|
|
28
|
+
return readWorkspaceConfig(cwd) ?? (basename(appletsDir) === "applets" ? readWorkspaceConfig(dirname(appletsDir)) : void 0);
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export { readWorkspaceConfig as n, findWorkspaceConfig as t };
|