@hitch42/applet 0.1.1-beta.15 → 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-BkhBHdMs.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-D5SBBd-j.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-MWjUbZnO.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-Dw7GEI5v.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-NcWw8SwL.js → project-CCVqCCgU.js} +3 -2
- 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/package.json +13 -1
- package/scaffold/.agents/skills/hitch-applets/references/markdown.md +20 -2
- package/dist/build-D7FDW8iC.js +0 -2
- package/dist/email-WA4jnzZ0.js +0 -211
- package/dist/email-protocol-DqJ6YCiC.d.ts +0 -230
- package/dist/generate-WYS-jtPH.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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as AnyWorkflowDefinition } from "../workflow-DurM6Fwx.js";
|
|
2
|
-
import {
|
|
2
|
+
import { d as ListEmailTrackersInput, n as TelephonyMessageReceivedEvent, r as CreateEmailPixelInput, t as EmailMessageReceivedEvent } from "../email-protocol-B9u_dh2p.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
//#region src/server/entry.d.ts
|
|
5
5
|
declare function createWorkerEntry(server: {
|
|
@@ -92,4 +92,13 @@ type Engine = {
|
|
|
92
92
|
declare class WorkflowFailedError extends Error {}
|
|
93
93
|
declare function runWorkflowJournal(engine: Engine, invoke: (journal: JournalEntry[]) => Promise<WorkflowOutcome>): Promise<unknown>;
|
|
94
94
|
//#endregion
|
|
95
|
+
//#region src/hitch/email-schemas.d.ts
|
|
96
|
+
declare const createEmailPixelInputSchema: z.ZodType<CreateEmailPixelInput, CreateEmailPixelInput>;
|
|
97
|
+
declare const markEmailTrackerSentBodySchema: z.ZodType<{
|
|
98
|
+
sentAt?: string;
|
|
99
|
+
}, {
|
|
100
|
+
sentAt?: string;
|
|
101
|
+
}>;
|
|
102
|
+
declare const listEmailTrackersInputSchema: z.ZodType<ListEmailTrackersInput, ListEmailTrackersInput>;
|
|
103
|
+
//#endregion
|
|
95
104
|
export { type Engine, type JournalEntry, WorkflowFailedError, type WorkflowInvocationRequest, type WorkflowOutcome, createEmailPixelInputSchema, createWorkerEntry, journalEntrySchema, listEmailTrackersInputSchema, markEmailTrackerSentBodySchema, runWorkflowJournal, workflowInvocationRequestSchema, workflowOutcomeSchema };
|
package/dist/_internal/server.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { C as markEmailTrackerSentBodySchema, g as listEmailTrackersInputSchema, t as createEmailPixelInputSchema } from "../email-WA4jnzZ0.js";
|
|
2
1
|
import { a as HITCH_ORG_CODE_HEADER, c as HITCH_SCHEDULE_HEADER, d as HITCH_USER_HEADER, f as HITCH_WORKFLOW_HEADER, i as HITCH_ORGANIZATION_HEADER, l as HITCH_SCHEDULE_RUN_HEADER, m as HITCH_WORKFLOW_RUN_HEADER, n as HITCH_APPLET_HEADER, o as HITCH_RELEASE_HEADER, p as HITCH_WORKFLOW_INVOCATION_HEADER, r as HITCH_APPLET_TOKEN_HEADER, u as HITCH_SESSION_HEADER } from "../headers-DkNr9MLJ.js";
|
|
3
2
|
import { a as getRequestContext, o as runWithRequestContext, r as toMilliseconds, t as MAX_DURATION_MS } from "../workflow-CTo2kZF1.js";
|
|
4
3
|
import { a as errorNote, c as requestIdFrom, i as createNoteLog, r as cloudflareRequestFields, s as publishNote, t as APP_CONTEXT_CHANNEL } from "../log-context-zu8U9Sjm.js";
|
|
5
4
|
import { a as workflowOutcomeSchema, i as workflowInvocationRequestSchema, n as runWorkflowJournal, r as journalEntrySchema, t as WorkflowFailedError } from "../workflow-engine-BOTzURKM.js";
|
|
6
|
-
import { z } from "zod";
|
|
7
5
|
import { RPCHandler } from "@orpc/server/fetch";
|
|
6
|
+
import { z } from "zod";
|
|
8
7
|
//#region src/server/telephony-protocol.ts
|
|
9
8
|
const telephonyMessageReceivedEventSchema = z.object({
|
|
10
9
|
eventId: z.string(),
|
|
@@ -258,4 +257,19 @@ function createWorkerEntry(server, options) {
|
|
|
258
257
|
} };
|
|
259
258
|
}
|
|
260
259
|
//#endregion
|
|
260
|
+
//#region src/hitch/email-schemas.ts
|
|
261
|
+
const emailTagKeySchema = z.string().regex(/^[A-Za-z][A-Za-z0-9_]{0,63}$/);
|
|
262
|
+
const emailTagsSchema = z.record(emailTagKeySchema, z.string().min(1).max(200)).refine((tags) => Object.keys(tags).length <= 10);
|
|
263
|
+
const createEmailPixelInputSchema = z.object({ tags: emailTagsSchema.optional() }).strict();
|
|
264
|
+
const markEmailTrackerSentBodySchema = z.object({ sentAt: z.iso.datetime({ offset: true }).optional() }).strict();
|
|
265
|
+
const listEmailTrackersInputSchema = z.object({
|
|
266
|
+
filter: emailTagsSchema.optional(),
|
|
267
|
+
sentAt: z.object({
|
|
268
|
+
$gte: z.iso.datetime({ offset: true }).optional(),
|
|
269
|
+
$lt: z.iso.datetime({ offset: true }).optional()
|
|
270
|
+
}).strict().optional(),
|
|
271
|
+
cursor: z.uuid().optional(),
|
|
272
|
+
limit: z.number().int().min(1).max(200).optional()
|
|
273
|
+
}).strict();
|
|
274
|
+
//#endregion
|
|
261
275
|
export { WorkflowFailedError, createEmailPixelInputSchema, createWorkerEntry, journalEntrySchema, listEmailTrackersInputSchema, markEmailTrackerSentBodySchema, runWorkflowJournal, workflowInvocationRequestSchema, workflowOutcomeSchema };
|
package/dist/applet.js
CHANGED
|
@@ -90,7 +90,7 @@ async function main() {
|
|
|
90
90
|
strict: true
|
|
91
91
|
});
|
|
92
92
|
if (positionals.length === 1 && positionals[0] === "login" && Object.keys(values).every((key) => key === "no-browser")) {
|
|
93
|
-
await (await import("./login-
|
|
93
|
+
await (await import("./login-CceGvlMi.js")).runLogin({ noBrowser: values["no-browser"] });
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
96
96
|
if (values["no-browser"] !== void 0) throw new Error(usage);
|
|
@@ -99,23 +99,23 @@ async function main() {
|
|
|
99
99
|
const noLogFlags = values.run === void 0 && values.filter === void 0 && values.level === void 0 && values.limit === void 0 && values.live === void 0 && values.search === void 0 && values.since === void 0;
|
|
100
100
|
const noModeFlags = values["client-only"] === void 0 && values["server-only"] === void 0;
|
|
101
101
|
if (positionals.length === 1 && positionals[0] === "bootstrap" && noModeFlags && noGitFlags && noVariableFlags && noLogFlags) {
|
|
102
|
-
await (await import("./init-
|
|
102
|
+
await (await import("./init-BGQ_bP44.js")).runBootstrap({ install: values["no-install"] !== true });
|
|
103
103
|
return;
|
|
104
104
|
}
|
|
105
105
|
if (positionals.length === 1 && positionals[0] === "generate" && values["no-install"] === void 0 && noModeFlags && noGitFlags && noVariableFlags && noLogFlags) {
|
|
106
|
-
await (await import("./generate-
|
|
106
|
+
await (await import("./generate-C9bWqdaB.js")).runGenerate();
|
|
107
107
|
return;
|
|
108
108
|
}
|
|
109
109
|
if (positionals.length === 1 && positionals[0] === "build" && values["no-install"] === void 0 && noModeFlags && noGitFlags && noVariableFlags && noLogFlags) {
|
|
110
|
-
await (await import("./build-
|
|
110
|
+
await (await import("./build-ueR7XjZT.js")).runBuild();
|
|
111
111
|
return;
|
|
112
112
|
}
|
|
113
113
|
if (positionals.length === 1 && positionals[0] === "deploy" && values["no-install"] === void 0 && noModeFlags && noGitFlags && noVariableFlags && noLogFlags) {
|
|
114
|
-
await (await import("./deploy-
|
|
114
|
+
await (await import("./deploy-C5JDIn2Y.js")).runDeploy({});
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
117
|
if (positionals.length === 2 && positionals[0] === "init" && noModeFlags && noVariableFlags && noLogFlags) {
|
|
118
|
-
await (await import("./init-
|
|
118
|
+
await (await import("./init-BGQ_bP44.js")).runInit(positionals[1], {
|
|
119
119
|
commit: values["no-commit"] !== true,
|
|
120
120
|
git: values["no-git"] !== true,
|
|
121
121
|
install: values["no-install"] !== true
|
|
@@ -123,7 +123,7 @@ async function main() {
|
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
125
125
|
if (positionals.length === 2 && positionals[0] === "new" && noGitFlags && noVariableFlags && noLogFlags) {
|
|
126
|
-
await (await import("./init-
|
|
126
|
+
await (await import("./init-BGQ_bP44.js")).runNew({
|
|
127
127
|
code: positionals[1],
|
|
128
128
|
clientOnly: values["client-only"],
|
|
129
129
|
install: values["no-install"] !== true,
|
|
@@ -132,7 +132,7 @@ async function main() {
|
|
|
132
132
|
return;
|
|
133
133
|
}
|
|
134
134
|
if ((positionals.length === 1 || positionals.length === 2) && positionals[0] === "logs" && values["no-install"] === void 0 && noModeFlags && noGitFlags && noVariableFlags) {
|
|
135
|
-
await (await import("./logs-
|
|
135
|
+
await (await import("./logs-DOkT5ots.js")).runLogs({
|
|
136
136
|
code: positionals[1],
|
|
137
137
|
filter: values.filter,
|
|
138
138
|
run: values.run,
|
|
@@ -145,14 +145,14 @@ async function main() {
|
|
|
145
145
|
return;
|
|
146
146
|
}
|
|
147
147
|
if (positionals.length === 3 && positionals[0] === "schedules" && positionals[1] === "run" && values["no-install"] === void 0 && noModeFlags && noGitFlags && noLogFlags && values.secret === void 0) {
|
|
148
|
-
await (await import("./schedules-
|
|
148
|
+
await (await import("./schedules-D6-3v-Az.js")).runSchedule({
|
|
149
149
|
code: positionals[2],
|
|
150
150
|
yes: values.yes
|
|
151
151
|
});
|
|
152
152
|
return;
|
|
153
153
|
}
|
|
154
154
|
if ((positionals.length === 3 || positionals.length === 4) && positionals[0] === "variables" && positionals[1] === "set" && values["no-install"] === void 0 && noModeFlags && noGitFlags && noLogFlags) {
|
|
155
|
-
await (await import("./variables-
|
|
155
|
+
await (await import("./variables-BSFZnLxY.js")).runVariablesSet({
|
|
156
156
|
key: positionals[2],
|
|
157
157
|
secret: values.secret,
|
|
158
158
|
value: positionals[3],
|
|
@@ -161,11 +161,11 @@ async function main() {
|
|
|
161
161
|
return;
|
|
162
162
|
}
|
|
163
163
|
if (positionals.length === 2 && positionals[0] === "variables" && positionals[1] === "list" && values["no-install"] === void 0 && noModeFlags && noGitFlags && noVariableFlags && noLogFlags) {
|
|
164
|
-
await (await import("./variables-
|
|
164
|
+
await (await import("./variables-BSFZnLxY.js")).runVariablesList();
|
|
165
165
|
return;
|
|
166
166
|
}
|
|
167
167
|
if (positionals.length === 3 && positionals[0] === "variables" && positionals[1] === "delete" && values["no-install"] === void 0 && noModeFlags && noGitFlags && values.secret === void 0 && noLogFlags) {
|
|
168
|
-
await (await import("./variables-
|
|
168
|
+
await (await import("./variables-BSFZnLxY.js")).runVariablesDelete({
|
|
169
169
|
key: positionals[2],
|
|
170
170
|
yes: values.yes
|
|
171
171
|
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { a as organizationInstanceUrl, c as resolveInstanceUrl, i as getCredentialsForOrg, o as rejectEnvironmentKey, r as bearerToken, s as resolveAuthForOrg, t as AuthenticationError } from "./store-B8N_8JIN.js";
|
|
2
|
+
import { t as loginWithDevice } from "./device-BgIfytaR.js";
|
|
3
|
+
import "./workspace-8yqXsvce.js";
|
|
4
|
+
//#region src/auth.ts
|
|
5
|
+
async function fetchWithOrgAuth(orgCode, request) {
|
|
6
|
+
const instanceUrl = await resolveInstanceUrl();
|
|
7
|
+
const organizationUrl = organizationInstanceUrl(instanceUrl, orgCode);
|
|
8
|
+
const auth = await resolveAuthForOrg(instanceUrl, orgCode);
|
|
9
|
+
if (auth !== void 0) {
|
|
10
|
+
const response = await request(organizationUrl, bearerToken(auth));
|
|
11
|
+
rejectEnvironmentKey(response, auth);
|
|
12
|
+
if (response.status !== 401) return response;
|
|
13
|
+
}
|
|
14
|
+
if (process.stdin.isTTY !== true) throw new AuthenticationError("Not logged in. Run applet login, or set HITCH_API_KEY.");
|
|
15
|
+
await loginWithDevice(instanceUrl, { expectedOrg: orgCode });
|
|
16
|
+
const credentials = await getCredentialsForOrg(instanceUrl, orgCode);
|
|
17
|
+
if (credentials === void 0) throw new Error(`login did not provide credentials for organization "${orgCode}"`);
|
|
18
|
+
return request(organizationUrl, bearerToken(credentials));
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { fetchWithOrgAuth as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as prepareAppletProjects, r as prepareAppletProject } from "./project-
|
|
1
|
+
import { i as prepareAppletProjects, r as prepareAppletProject } from "./project-CCVqCCgU.js";
|
|
2
2
|
import { rm } from "node:fs/promises";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { spawn } from "node:child_process";
|
package/dist/client.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as NarrowContract } from "./contract-
|
|
1
|
+
import { d as RegisteredRouter, l as RegisteredContract, u as RegisteredManifest } from "./manifest-BSUtHbu-.js";
|
|
2
|
+
import { t as NarrowContract } from "./contract-D9wEvxGI.js";
|
|
3
3
|
import { t as WithBatchRecorder } from "./batch-Qa4B4-Yp.js";
|
|
4
|
-
import { o as WithFilesUpload } from "./upload-
|
|
4
|
+
import { o as WithFilesUpload } from "./upload-D_kkmne_.js";
|
|
5
5
|
import { RouterClient } from "@orpc/server";
|
|
6
6
|
import { ContractRouterClient } from "@orpc/contract";
|
|
7
7
|
import { NestedClient } from "@orpc/client";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/hitch/contract.ts
|
|
2
|
+
function isObject(value) {
|
|
3
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
function pruneContract(contract, manifest) {
|
|
6
|
+
if (!isObject(contract) || !isObject(contract.records)) throw new Error("Hitch contract must contain a \"records\" router.");
|
|
7
|
+
const contractRecords = contract.records;
|
|
8
|
+
const records = Object.fromEntries(manifest.objects.map((manifestObject) => {
|
|
9
|
+
const procedures = contractRecords[manifestObject.code];
|
|
10
|
+
if (!isObject(procedures)) throw new Error(`Hitch contract object "${manifestObject.code}" is missing.`);
|
|
11
|
+
const pruned = { ...procedures };
|
|
12
|
+
if (!manifestObject.canCreate) delete pruned.create;
|
|
13
|
+
if (!manifestObject.canDelete) delete pruned.delete;
|
|
14
|
+
if (manifestObject.readWrite.length === 0) {
|
|
15
|
+
delete pruned.update;
|
|
16
|
+
delete pruned.updateWhere;
|
|
17
|
+
}
|
|
18
|
+
return [manifestObject.code, pruned];
|
|
19
|
+
}));
|
|
20
|
+
return Object.assign({}, contract, { records });
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { pruneContract as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as AppletManifest } from "./manifest-
|
|
1
|
+
import { n as AppletManifest } from "./manifest-BSUtHbu-.js";
|
|
2
2
|
import { AnyContractRouter } from "@orpc/contract";
|
|
3
3
|
//#region src/hitch/contract.d.ts
|
|
4
4
|
type ManifestObjectFor<Manifest, Code> = Manifest extends {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { n as
|
|
1
|
+
import { n as readWorkspaceConfig } from "./workspace-8yqXsvce.js";
|
|
2
|
+
import { t as fetchWithOrgAuth } from "./auth-D3CrCtZq.js";
|
|
3
|
+
import { a as loadGitHubActionsSources, n as githubActionsContext, r as hitchResponseError, t as fetchWithGitHubActions } from "./ci-B5-mH-YW.js";
|
|
4
|
+
import { n as buildPreparedApplet, r as prepareWorkspaceBuild, t as buildApplet } from "./build-XHoa_sJL.js";
|
|
4
5
|
import { createRequire } from "node:module";
|
|
5
6
|
import { createHash } from "node:crypto";
|
|
6
7
|
import { existsSync } from "node:fs";
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { l as setCredentials, o as rejectEnvironmentKey, t as AuthenticationError } from "./store-B8N_8JIN.js";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
//#region ../shared/auth/src/device.mts
|
|
4
|
+
const CLIENT_ID = "hitch-cli";
|
|
5
|
+
const DEVICE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
|
|
6
|
+
function defaultOpenUrl(url) {
|
|
7
|
+
try {
|
|
8
|
+
const child = spawn(process.platform === "darwin" ? "open" : "xdg-open", [url], {
|
|
9
|
+
detached: true,
|
|
10
|
+
stdio: "ignore"
|
|
11
|
+
});
|
|
12
|
+
child.on("error", () => void 0);
|
|
13
|
+
child.unref();
|
|
14
|
+
} catch {}
|
|
15
|
+
}
|
|
16
|
+
function requiredObject(value, message) {
|
|
17
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error(message);
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
async function jsonResponse(response) {
|
|
21
|
+
try {
|
|
22
|
+
return requiredObject(await response.json(), "Expected a JSON object response.");
|
|
23
|
+
} catch (error) {
|
|
24
|
+
throw new Error(`Invalid JSON response from ${response.url || "Hitch"}.`, { cause: error });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async function postJson(fetcher, url, body) {
|
|
28
|
+
const response = await fetcher(url, {
|
|
29
|
+
method: "POST",
|
|
30
|
+
headers: { "content-type": "application/json" },
|
|
31
|
+
body: JSON.stringify(body)
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
body: await jsonResponse(response),
|
|
35
|
+
response
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function parseDeviceCode(value) {
|
|
39
|
+
if (typeof value.device_code !== "string" || typeof value.user_code !== "string" || typeof value.verification_uri !== "string" || typeof value.verification_uri_complete !== "string" || typeof value.expires_in !== "number" || value.interval !== void 0 && typeof value.interval !== "number") throw new AuthenticationError("Hitch returned an invalid device authorization response.");
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
function parseToken(value) {
|
|
43
|
+
return typeof value.access_token === "string" && typeof value.token_type === "string" ? value : void 0;
|
|
44
|
+
}
|
|
45
|
+
function parseDeviceOrganization(value) {
|
|
46
|
+
if (typeof value.organizationId !== "string" || typeof value.orgCode !== "string") throw new AuthenticationError("Hitch returned an invalid device organization response.");
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
function parseMe(value) {
|
|
50
|
+
const person = requiredObject(value.person, "Hitch returned an invalid identity response.");
|
|
51
|
+
if (typeof person.id !== "string" || typeof person.firstName !== "string" || typeof person.lastName !== "string" || typeof person.email !== "string" || !Array.isArray(value.memberships)) throw new Error("Hitch returned an invalid identity response.");
|
|
52
|
+
const organization = value.organization === void 0 || value.organization === null ? null : requiredObject(value.organization, "Hitch returned an invalid identity response.");
|
|
53
|
+
if (organization !== null && (typeof organization.id !== "string" || typeof organization.code !== "string")) throw new Error("Hitch returned an invalid identity response.");
|
|
54
|
+
return {
|
|
55
|
+
memberships: value.memberships.map((membership) => {
|
|
56
|
+
const item = requiredObject(membership, "Hitch returned an invalid membership.");
|
|
57
|
+
if (typeof item.orgId !== "string" || typeof item.code !== "string" || typeof item.name !== "string") throw new Error("Hitch returned an invalid membership.");
|
|
58
|
+
return item;
|
|
59
|
+
}),
|
|
60
|
+
organization,
|
|
61
|
+
person
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
async function fetchMe(fetcher, instanceUrl, auth) {
|
|
65
|
+
const response = await fetcher(`${instanceUrl}/api/v1/me`, { headers: { Authorization: `Bearer ${auth.accessToken}` } });
|
|
66
|
+
rejectEnvironmentKey(response, auth);
|
|
67
|
+
if (auth.credentialType === "apiKey" && response.status === 401) throw new AuthenticationError("API key rejected.");
|
|
68
|
+
const body = await jsonResponse(response);
|
|
69
|
+
if (!response.ok) throw new AuthenticationError(typeof body.message === "string" ? body.message : `Failed to load the signed-in identity (${response.status}).`);
|
|
70
|
+
return parseMe(body);
|
|
71
|
+
}
|
|
72
|
+
async function pollForToken(fetcher, instanceUrl, device, now, sleep) {
|
|
73
|
+
const deadline = now() + device.expires_in * 1e3;
|
|
74
|
+
let interval = (device.interval ?? 5) * 1e3;
|
|
75
|
+
while (now() < deadline) {
|
|
76
|
+
await sleep(Math.min(interval, deadline - now()));
|
|
77
|
+
if (now() >= deadline) break;
|
|
78
|
+
const { body, response } = await postJson(fetcher, `${instanceUrl}/api/auth/device/token`, {
|
|
79
|
+
grant_type: DEVICE_GRANT,
|
|
80
|
+
device_code: device.device_code,
|
|
81
|
+
client_id: CLIENT_ID
|
|
82
|
+
});
|
|
83
|
+
const token = parseToken(body);
|
|
84
|
+
if (response.ok && token !== void 0) return token;
|
|
85
|
+
switch (body.error) {
|
|
86
|
+
case "authorization_pending": break;
|
|
87
|
+
case "slow_down":
|
|
88
|
+
interval += 5e3;
|
|
89
|
+
break;
|
|
90
|
+
case "expired_token": throw new AuthenticationError("The sign-in request expired. Run hitch login again.");
|
|
91
|
+
case "access_denied": throw new AuthenticationError("Sign-in was denied.");
|
|
92
|
+
default: throw new AuthenticationError(typeof body.error_description === "string" ? body.error_description : `Sign-in failed (${response.status}).`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
throw new AuthenticationError("The sign-in request expired. Run hitch login again.");
|
|
96
|
+
}
|
|
97
|
+
async function loginWithDevice(instanceUrl, options = {}) {
|
|
98
|
+
const fetcher = globalThis.fetch;
|
|
99
|
+
const now = Date.now;
|
|
100
|
+
const { body, response } = await postJson(fetcher, `${instanceUrl}/api/auth/device/code`, { client_id: CLIENT_ID });
|
|
101
|
+
if (!response.ok) throw new AuthenticationError(typeof body.error_description === "string" ? body.error_description : `Could not start sign-in (${response.status}).`);
|
|
102
|
+
const device = parseDeviceCode(body);
|
|
103
|
+
const verificationUrl = options.expectedOrg === void 0 ? device.verification_uri_complete : `${device.verification_uri_complete}&org=${encodeURIComponent(options.expectedOrg)}`;
|
|
104
|
+
console.log(`Open ${verificationUrl} and confirm code ${device.user_code}.`);
|
|
105
|
+
if (!options.noBrowser) try {
|
|
106
|
+
defaultOpenUrl(verificationUrl);
|
|
107
|
+
} catch {}
|
|
108
|
+
const token = await pollForToken(fetcher, instanceUrl, device, now, (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
|
|
109
|
+
const { body: organizationBody, response: organizationResponse } = await postJson(fetcher, `${instanceUrl}/api/auth/device/organization`, { device_code: device.device_code });
|
|
110
|
+
if (!organizationResponse.ok) throw new AuthenticationError("The sign-in did not resolve an organization.");
|
|
111
|
+
const organization = parseDeviceOrganization(organizationBody);
|
|
112
|
+
const me = await fetchMe(fetcher, instanceUrl, {
|
|
113
|
+
accessToken: token.access_token,
|
|
114
|
+
credentialType: "session",
|
|
115
|
+
source: "store"
|
|
116
|
+
});
|
|
117
|
+
const membership = me.memberships.find(({ orgId }) => orgId === organization.organizationId);
|
|
118
|
+
if (membership === void 0) throw new AuthenticationError("The device session is not scoped to a Hitch organization.");
|
|
119
|
+
const credentials = {
|
|
120
|
+
type: "session",
|
|
121
|
+
accessToken: token.access_token,
|
|
122
|
+
expiresAt: token.expires_in === void 0 ? void 0 : now() + token.expires_in * 1e3,
|
|
123
|
+
organizationId: organization.organizationId,
|
|
124
|
+
orgCode: organization.orgCode
|
|
125
|
+
};
|
|
126
|
+
await setCredentials(instanceUrl, credentials);
|
|
127
|
+
if (options.expectedOrg !== void 0 && organization.orgCode !== options.expectedOrg) throw new AuthenticationError(`logged in to "${organization.orgCode}" but this applet targets "${options.expectedOrg}" — confirm the right organization on the device page`);
|
|
128
|
+
return {
|
|
129
|
+
credentials,
|
|
130
|
+
me,
|
|
131
|
+
membership
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
//#endregion
|
|
135
|
+
export { loginWithDevice as t };
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { ContractProcedure, Meta, Schema } from "@orpc/contract";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/hitch/email.d.ts
|
|
4
|
+
type EmailTags = Record<string, string>;
|
|
5
|
+
type CreateEmailPixelInput = {
|
|
6
|
+
tags?: EmailTags;
|
|
7
|
+
};
|
|
8
|
+
type CreateEmailPixelOutput = {
|
|
9
|
+
id: string;
|
|
10
|
+
url: string;
|
|
11
|
+
};
|
|
12
|
+
type GetEmailTrackerInput = {
|
|
13
|
+
id: string;
|
|
14
|
+
};
|
|
15
|
+
type MarkEmailTrackerSentInput = {
|
|
16
|
+
id: string;
|
|
17
|
+
sentAt?: string;
|
|
18
|
+
};
|
|
19
|
+
type EmailTracker = {
|
|
20
|
+
id: string;
|
|
21
|
+
url: string;
|
|
22
|
+
tags: EmailTags;
|
|
23
|
+
createdAt: string;
|
|
24
|
+
sentAt: string | null;
|
|
25
|
+
firstHitAt: string | null;
|
|
26
|
+
lastHitAt: string | null;
|
|
27
|
+
};
|
|
28
|
+
type ListEmailTrackersInput = {
|
|
29
|
+
filter?: EmailTags;
|
|
30
|
+
sentAt?: {
|
|
31
|
+
$gte?: string;
|
|
32
|
+
$lt?: string;
|
|
33
|
+
};
|
|
34
|
+
cursor?: string;
|
|
35
|
+
limit?: number;
|
|
36
|
+
};
|
|
37
|
+
type ListEmailTrackersOutput = {
|
|
38
|
+
trackers: EmailTracker[];
|
|
39
|
+
nextCursor: string | null;
|
|
40
|
+
};
|
|
41
|
+
type EmailContract = {
|
|
42
|
+
mailboxes: {
|
|
43
|
+
list: ContractProcedure<Schema<void, void>, Schema<HitchEmailMailboxesOutput, HitchEmailMailboxesOutput>, Record<never, never>, Meta>;
|
|
44
|
+
};
|
|
45
|
+
threads: {
|
|
46
|
+
list: ContractProcedure<Schema<ListHitchEmailThreadsInput, ListHitchEmailThreadsInput>, Schema<HitchEmailThreadsOutput, HitchEmailThreadsOutput>, Record<never, never>, Meta>;
|
|
47
|
+
get: ContractProcedure<Schema<HitchEmailIdInput, HitchEmailIdInput>, Schema<HitchEmailThread, HitchEmailThread>, Record<never, never>, Meta>;
|
|
48
|
+
};
|
|
49
|
+
messages: {
|
|
50
|
+
/** Newest first. Pass threadId to page through one thread. */
|
|
51
|
+
list: ContractProcedure<Schema<ListHitchEmailMessagesInput, ListHitchEmailMessagesInput>, Schema<HitchEmailMessagesOutput, HitchEmailMessagesOutput>, Record<never, never>, Meta>;
|
|
52
|
+
get: ContractProcedure<Schema<HitchEmailIdInput, HitchEmailIdInput>, Schema<HitchEmailMessage, HitchEmailMessage>, Record<never, never>, Meta>;
|
|
53
|
+
send: ContractProcedure<Schema<SendHitchEmailInput, SendHitchEmailInput>, Schema<HitchEmailMessage, HitchEmailMessage>, Record<never, never>, Meta>;
|
|
54
|
+
raw: ContractProcedure<Schema<HitchEmailIdInput, HitchEmailIdInput>, Schema<HitchEmailRawOutput, HitchEmailRawOutput>, Record<never, never>, Meta>;
|
|
55
|
+
};
|
|
56
|
+
attachments: {
|
|
57
|
+
get: ContractProcedure<Schema<HitchEmailIdInput, HitchEmailIdInput>, Schema<HitchEmailAttachmentOutput, HitchEmailAttachmentOutput>, Record<never, never>, Meta>;
|
|
58
|
+
};
|
|
59
|
+
tracking: {
|
|
60
|
+
createPixel: ContractProcedure<Schema<CreateEmailPixelInput | undefined, CreateEmailPixelInput | undefined>, Schema<CreateEmailPixelOutput, CreateEmailPixelOutput>, Record<never, never>, Meta>;
|
|
61
|
+
markSent: ContractProcedure<Schema<MarkEmailTrackerSentInput, MarkEmailTrackerSentInput>, Schema<EmailTracker, EmailTracker>, Record<never, never>, Meta>;
|
|
62
|
+
get: ContractProcedure<Schema<GetEmailTrackerInput, GetEmailTrackerInput>, Schema<EmailTracker, EmailTracker>, Record<never, never>, Meta>;
|
|
63
|
+
list: ContractProcedure<Schema<ListEmailTrackersInput | undefined, ListEmailTrackersInput | undefined>, Schema<ListEmailTrackersOutput, ListEmailTrackersOutput>, Record<never, never>, Meta>;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
declare function emailContract(appletCode: string): EmailContract;
|
|
67
|
+
type HitchEmailAddress = {
|
|
68
|
+
address: string;
|
|
69
|
+
name: string | null;
|
|
70
|
+
};
|
|
71
|
+
type HitchMailbox = {
|
|
72
|
+
id: string;
|
|
73
|
+
address: string;
|
|
74
|
+
label: string;
|
|
75
|
+
enabled: boolean;
|
|
76
|
+
receive: boolean;
|
|
77
|
+
canRead: boolean;
|
|
78
|
+
canSend: boolean;
|
|
79
|
+
};
|
|
80
|
+
type HitchEmailAttachment = {
|
|
81
|
+
id: string;
|
|
82
|
+
filename: string;
|
|
83
|
+
contentType: string;
|
|
84
|
+
sizeBytes: number;
|
|
85
|
+
contentId: string | null;
|
|
86
|
+
inline: boolean;
|
|
87
|
+
};
|
|
88
|
+
type HitchEmailMessage = {
|
|
89
|
+
id: string;
|
|
90
|
+
mailboxId: string;
|
|
91
|
+
threadId: string;
|
|
92
|
+
direction: "inbound" | "outbound";
|
|
93
|
+
status: "received" | "unknown" | "sent" | "failed";
|
|
94
|
+
messageId: string | null;
|
|
95
|
+
inReplyTo: string | null;
|
|
96
|
+
references: string[];
|
|
97
|
+
from: HitchEmailAddress;
|
|
98
|
+
to: HitchEmailAddress[];
|
|
99
|
+
cc: HitchEmailAddress[];
|
|
100
|
+
bcc: HitchEmailAddress[];
|
|
101
|
+
replyTo: HitchEmailAddress[];
|
|
102
|
+
subject: string;
|
|
103
|
+
text: string | null;
|
|
104
|
+
html: string | null;
|
|
105
|
+
sentAt: string | null;
|
|
106
|
+
automated: string | null;
|
|
107
|
+
authentication: {
|
|
108
|
+
spf: string | null;
|
|
109
|
+
dkim: string | null;
|
|
110
|
+
dmarc: string | null;
|
|
111
|
+
} | null;
|
|
112
|
+
parseError: string | null;
|
|
113
|
+
attachments: HitchEmailAttachment[];
|
|
114
|
+
userId: string | null;
|
|
115
|
+
appletId: string | null;
|
|
116
|
+
requestId: string | null;
|
|
117
|
+
errorDetail: string | null;
|
|
118
|
+
createdAt: string;
|
|
119
|
+
};
|
|
120
|
+
type HitchEmailThread = {
|
|
121
|
+
id: string;
|
|
122
|
+
mailboxId: string;
|
|
123
|
+
subject: string;
|
|
124
|
+
messageCount: number;
|
|
125
|
+
lastMessageAt: string;
|
|
126
|
+
createdAt: string;
|
|
127
|
+
};
|
|
128
|
+
type ListHitchEmailThreadsInput = {
|
|
129
|
+
mailboxId?: string;
|
|
130
|
+
cursor?: string;
|
|
131
|
+
limit?: number;
|
|
132
|
+
};
|
|
133
|
+
type ListHitchEmailMessagesInput = {
|
|
134
|
+
mailboxId?: string;
|
|
135
|
+
address?: string;
|
|
136
|
+
threadId?: string;
|
|
137
|
+
direction?: "inbound" | "outbound";
|
|
138
|
+
since?: string;
|
|
139
|
+
until?: string;
|
|
140
|
+
cursor?: string;
|
|
141
|
+
limit?: number;
|
|
142
|
+
};
|
|
143
|
+
type SendHitchEmailInput = {
|
|
144
|
+
mailboxId: string;
|
|
145
|
+
to?: string[];
|
|
146
|
+
cc?: string[];
|
|
147
|
+
bcc?: string[];
|
|
148
|
+
subject?: string;
|
|
149
|
+
text?: string;
|
|
150
|
+
html?: string;
|
|
151
|
+
replyToMessageId?: string;
|
|
152
|
+
attachmentFileIds?: string[];
|
|
153
|
+
requestId?: string;
|
|
154
|
+
};
|
|
155
|
+
type HitchEmailIdInput = {
|
|
156
|
+
id: string;
|
|
157
|
+
};
|
|
158
|
+
type HitchEmailMailboxesOutput = {
|
|
159
|
+
data: HitchMailbox[];
|
|
160
|
+
};
|
|
161
|
+
type HitchEmailThreadsOutput = {
|
|
162
|
+
data: HitchEmailThread[];
|
|
163
|
+
nextCursor: string | null;
|
|
164
|
+
};
|
|
165
|
+
type HitchEmailMessagesOutput = {
|
|
166
|
+
data: HitchEmailMessage[];
|
|
167
|
+
nextCursor: string | null;
|
|
168
|
+
};
|
|
169
|
+
type HitchEmailRawOutput = {
|
|
170
|
+
url: string;
|
|
171
|
+
};
|
|
172
|
+
type HitchEmailAttachmentOutput = {
|
|
173
|
+
url: string;
|
|
174
|
+
filename: string;
|
|
175
|
+
contentType: string;
|
|
176
|
+
sizeBytes: number;
|
|
177
|
+
};
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region src/server/telephony-protocol.d.ts
|
|
180
|
+
/** Handle events at least once and without order. Use eventId for idempotency.
|
|
181
|
+
* Read the message with hitch.telephony.messages.get({ id: messageId }). */
|
|
182
|
+
type TelephonyMessageReceivedEvent = {
|
|
183
|
+
eventId: string;
|
|
184
|
+
type: "message.received";
|
|
185
|
+
messageId: string;
|
|
186
|
+
numberId: string;
|
|
187
|
+
occurredAt: string;
|
|
188
|
+
};
|
|
189
|
+
//#endregion
|
|
190
|
+
//#region src/server/email-protocol.d.ts
|
|
191
|
+
/** Handle events at least once and without order. Use eventId for idempotency.
|
|
192
|
+
* Read the message with hitch.email.messages.get({ id: messageId }). */
|
|
193
|
+
type EmailMessageReceivedEvent = {
|
|
194
|
+
eventId: string;
|
|
195
|
+
type: "message.received";
|
|
196
|
+
messageId: string;
|
|
197
|
+
mailboxId: string;
|
|
198
|
+
threadId: string;
|
|
199
|
+
occurredAt: string;
|
|
200
|
+
};
|
|
201
|
+
//#endregion
|
|
202
|
+
export { EmailTracker as a, HitchEmailMessage as c, ListEmailTrackersInput as d, ListEmailTrackersOutput as f, emailContract as g, SendHitchEmailInput as h, EmailContract as i, HitchEmailThread as l, ListHitchEmailThreadsInput as m, TelephonyMessageReceivedEvent as n, HitchEmailAddress as o, ListHitchEmailMessagesInput as p, CreateEmailPixelInput as r, HitchEmailAttachment as s, EmailMessageReceivedEvent as t, HitchMailbox as u };
|