@hitch42/applet 0.1.0-beta.3 → 0.1.0-beta.9
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 +14 -1
- package/dist/_internal/server.js +23 -74
- package/dist/applet.js +53 -29
- package/dist/build-BfbTbt_r.js +2 -0
- package/dist/{build-Ch3pALLA.js → build-CAb26kMY.js} +30 -14
- package/dist/ci-5qWl7lT3.js +68 -0
- package/dist/client.d.ts +3 -3
- package/dist/{contract-DMrpbaLJ.d.ts → contract-rP_eV9PK.d.ts} +1 -1
- package/dist/{deploy-DP2Bg6nt.js → deploy-D_OFJ6-Q.js} +193 -30
- package/dist/email-DBTVhNB_.js +62 -0
- package/dist/generate-BgepN4-b.js +2 -0
- package/dist/{generate-BuszZ9-U.js → generate-CBPnHoqX.js} +40 -30
- package/dist/index.d.ts +122 -11
- package/dist/index.js +141 -26
- package/dist/{init-BjWWDqqX.js → init-tfzwuYnk.js} +12 -8
- package/dist/load-config-BYSxDVAl.js +27 -0
- package/dist/login-C7RKmmTe.js +12 -0
- package/dist/{logs-m00pzr_O.js → logs-DgjgAxj2.js} +3 -3
- package/dist/{manifest-DyoJ1MIL.js → manifest-Bp87Uz0a.js} +6 -0
- package/dist/{manifest-BQCAxATS.d.ts → manifest-DD_SGkS6.d.ts} +5 -0
- package/dist/me-BW7Paary.js +32 -0
- package/dist/{project-MN3mFxeE.js → project-D6XHllmC.js} +26 -20
- package/dist/prompt-ChS-grYO.js +29 -0
- package/dist/react.d.ts +1 -1
- package/dist/react.js +40 -42
- package/dist/schedules-dhU1yyFr.js +42 -0
- package/dist/server.d.ts +3 -3
- package/dist/telephony-protocol-D-nf3Gca.d.ts +76 -0
- package/dist/telephony.d.ts +77 -0
- package/dist/telephony.js +607 -0
- package/dist/{upload-YbRKEqR6.d.ts → upload-DKZzz5kW.d.ts} +6 -6
- package/dist/{variables-CP7Ce3Np.js → variables-Bwnr7oxf.js} +3 -28
- package/dist/vite.d.ts +1 -1
- package/dist/vite.js +458 -27
- package/dist/workflow-engine-MogejNfF.js +117 -0
- package/dist/{auth-DaVK0k5R.js → workspace-XVtM12ZC.js} +44 -4
- package/package.json +6 -1
- package/scaffold/.agents/skills/hitch-applets/SKILL.md +34 -15
- package/scaffold/.agents/skills/hitch-applets/references/email-tracking.md +49 -0
- package/scaffold/.agents/skills/hitch-applets/references/files.md +3 -3
- package/scaffold/.agents/skills/hitch-applets/references/manifest.md +21 -2
- package/scaffold/.agents/skills/hitch-applets/references/performance.md +74 -0
- package/scaffold/.agents/skills/hitch-applets/references/records.md +30 -8
- package/scaffold/.agents/skills/hitch-applets/references/schedules.md +7 -2
- package/scaffold/.agents/skills/hitch-applets/references/server.md +0 -3
- package/scaffold/.agents/skills/hitch-applets/references/workflows.md +11 -3
- package/scaffold/.agents/skills/hitch-cli/SKILL.md +129 -0
- package/scaffold/.agents/skills/hitch-data/SKILL.md +51 -0
- package/scaffold/.agents/skills/hitch-data/references/attributes.md +121 -0
- package/scaffold/.agents/skills/hitch-data/references/limits.md +64 -0
- package/scaffold/.agents/skills/hitch-theory/SKILL.md +150 -0
- package/scaffold/.github/workflows/hitch.yml +1 -7
- package/dist/build-BqoQ-uNz.js +0 -2
- package/dist/generate-CRVA0Ll5.js +0 -2
- package/dist/load-config-BnxocLAQ.js +0 -56
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as
|
|
1
|
+
import { i as readWorkspaceConfig, n as fetchWithOrgAuth } from "./workspace-XVtM12ZC.js";
|
|
2
|
+
import { a as loadGitHubActionsSources, n as githubActionsContext, r as hitchResponseError, t as fetchWithGitHubActions } from "./ci-5qWl7lT3.js";
|
|
3
|
+
import { n as buildPreparedApplet, r as prepareWorkspaceBuild, t as buildApplet } from "./build-CAb26kMY.js";
|
|
3
4
|
import { createRequire } from "node:module";
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
4
6
|
import { readFile, readdir } from "node:fs/promises";
|
|
5
7
|
import { relative, resolve, sep } from "node:path";
|
|
6
8
|
import { execFile } from "node:child_process";
|
|
@@ -2311,6 +2313,12 @@ var import_tar_stream = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin((
|
|
|
2311
2313
|
exports.pack = require_pack();
|
|
2312
2314
|
})))(), 1);
|
|
2313
2315
|
const execFileAsync = promisify(execFile);
|
|
2316
|
+
var WorkspaceDeployError = class extends Error {
|
|
2317
|
+
constructor(failedApplets) {
|
|
2318
|
+
super(`Failed to deploy: ${failedApplets.join(", ")}. See the applet logs above.`);
|
|
2319
|
+
this.name = "WorkspaceDeployError";
|
|
2320
|
+
}
|
|
2321
|
+
};
|
|
2314
2322
|
function isErrno(error, code) {
|
|
2315
2323
|
return error instanceof Error && "code" in error && error.code === code;
|
|
2316
2324
|
}
|
|
@@ -2355,14 +2363,17 @@ async function createBundle(distDir) {
|
|
|
2355
2363
|
body.set(bundle);
|
|
2356
2364
|
return body;
|
|
2357
2365
|
}
|
|
2358
|
-
|
|
2359
|
-
|
|
2366
|
+
function gitEnvironment() {
|
|
2367
|
+
return Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith("GIT_")));
|
|
2368
|
+
}
|
|
2369
|
+
async function readGitInfo(appletRoot) {
|
|
2370
|
+
const env = gitEnvironment();
|
|
2360
2371
|
try {
|
|
2361
2372
|
const [{ stdout: status }, { stdout: sha }] = await Promise.all([execFileAsync("git", ["status", "--porcelain"], {
|
|
2362
|
-
cwd:
|
|
2373
|
+
cwd: appletRoot,
|
|
2363
2374
|
env
|
|
2364
2375
|
}), execFileAsync("git", ["rev-parse", "HEAD"], {
|
|
2365
|
-
cwd:
|
|
2376
|
+
cwd: appletRoot,
|
|
2366
2377
|
env
|
|
2367
2378
|
})]);
|
|
2368
2379
|
return {
|
|
@@ -2373,44 +2384,196 @@ async function readGitInfo() {
|
|
|
2373
2384
|
return { gitDirty: false };
|
|
2374
2385
|
}
|
|
2375
2386
|
}
|
|
2376
|
-
async function
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
return /* @__PURE__ */ new Error(`Deploy failed (${response.status})${details ? `: ${details}` : ""}.`);
|
|
2387
|
+
async function repositoryDirectory(appletRoot) {
|
|
2388
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "--show-toplevel"], {
|
|
2389
|
+
cwd: appletRoot,
|
|
2390
|
+
env: gitEnvironment()
|
|
2391
|
+
});
|
|
2392
|
+
const directory = relative(stdout.trim(), appletRoot).split(sep).join("/") || ".";
|
|
2393
|
+
if (directory === ".." || directory.startsWith("../")) throw new Error(`${appletRoot} is outside its git repository ${stdout.trim()}.`);
|
|
2394
|
+
return directory;
|
|
2385
2395
|
}
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2396
|
+
function releaseForm(manifest, bundle) {
|
|
2397
|
+
const body = new FormData();
|
|
2398
|
+
body.set("manifest", manifest.raw);
|
|
2399
|
+
body.set("bundle", new File([bundle], "bundle.tar.gz", { type: "application/gzip" }));
|
|
2400
|
+
return body;
|
|
2401
|
+
}
|
|
2402
|
+
async function deployWithOrgAuth(appletRoot, distDir, manifest, options) {
|
|
2391
2403
|
const fetcher = options.fetch ?? globalThis.fetch;
|
|
2392
|
-
const
|
|
2393
|
-
|
|
2394
|
-
body
|
|
2395
|
-
body.set("bundle", new File([bundle], "bundle.tar.gz", { type: "application/gzip" }));
|
|
2404
|
+
const [bundle, git] = await Promise.all([createBundle(distDir), readGitInfo(appletRoot)]);
|
|
2405
|
+
return fetchWithOrgAuth(manifest.org, options, (instanceUrl, accessToken) => {
|
|
2406
|
+
const body = releaseForm(manifest, bundle);
|
|
2396
2407
|
body.set("gitDirty", String(git.gitDirty));
|
|
2397
2408
|
if (git.gitSha !== void 0) body.set("gitSha", git.gitSha);
|
|
2398
|
-
if (options.changesetId !== void 0) body.set("changeset_id", options.changesetId);
|
|
2399
2409
|
return fetcher(`${instanceUrl}/api/v1/applets/${manifest.code}/releases`, {
|
|
2400
2410
|
method: "POST",
|
|
2401
2411
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
2402
2412
|
body
|
|
2403
2413
|
});
|
|
2404
2414
|
});
|
|
2405
|
-
|
|
2415
|
+
}
|
|
2416
|
+
async function deployWithGitHubActions(context, appletRoot, distDir, manifest, options) {
|
|
2417
|
+
const fetcher = options.fetch ?? globalThis.fetch;
|
|
2418
|
+
const [bundle, sourceDirectory] = await Promise.all([createBundle(distDir), repositoryDirectory(appletRoot)]);
|
|
2419
|
+
return fetchWithGitHubActions(context, options, (instanceUrl, accessToken) => {
|
|
2420
|
+
const body = releaseForm(manifest, bundle);
|
|
2421
|
+
body.set("source_directory", sourceDirectory);
|
|
2422
|
+
return fetcher(`${instanceUrl}/api/ci/v1/applets/${manifest.code}/releases`, {
|
|
2423
|
+
method: "POST",
|
|
2424
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
2425
|
+
body
|
|
2426
|
+
});
|
|
2427
|
+
});
|
|
2428
|
+
}
|
|
2429
|
+
async function deployApplet(appletRoot, options, logPrefix = "") {
|
|
2430
|
+
const github = githubActionsContext(options.store?.env ?? process.env);
|
|
2431
|
+
if (options.dist === void 0) await buildApplet(appletRoot);
|
|
2432
|
+
const distDir = resolve(appletRoot, options.dist ?? ".hitch/dist");
|
|
2433
|
+
const manifest = await readManifest(distDir);
|
|
2434
|
+
const response = github === void 0 ? await deployWithOrgAuth(appletRoot, distDir, manifest, options) : await deployWithGitHubActions(github, appletRoot, distDir, manifest, options);
|
|
2435
|
+
if (!response.ok) throw await hitchResponseError(response, "Deploy failed");
|
|
2406
2436
|
const result = await response.json();
|
|
2407
|
-
console.log(
|
|
2437
|
+
console.log(`${logPrefix}Deployed release ${result.data.id} (${result.promoted ? "promoted" : "not promoted"}).`);
|
|
2438
|
+
if (result.changeset_id !== void 0) console.log(`${logPrefix}The release goes live when changeset ${result.changeset_id} applies.`);
|
|
2408
2439
|
if (result.warnings?.length) {
|
|
2409
|
-
for (const warning of result.warnings) console.warn(
|
|
2410
|
-
console.warn(
|
|
2440
|
+
for (const warning of result.warnings) console.warn(`${logPrefix}Warning: ${warning.message}`);
|
|
2441
|
+
console.warn(`${logPrefix}Changeset ${result.changeset_id} cannot apply until the applet and the schema agree.`);
|
|
2411
2442
|
}
|
|
2412
|
-
if (result.unset_variables?.length) console.warn(
|
|
2443
|
+
if (result.unset_variables?.length) console.warn(`${logPrefix}Warning: declared variables not set: ${result.unset_variables.join(", ")}. Set them with applet variables set.`);
|
|
2413
2444
|
return result;
|
|
2414
2445
|
}
|
|
2446
|
+
async function workspaceAppletRoots(workspaceRoot) {
|
|
2447
|
+
const appletsDir = resolve(workspaceRoot, "applets");
|
|
2448
|
+
const roots = (await readdir(appletsDir, { withFileTypes: true }).catch((error) => {
|
|
2449
|
+
if (isErrno(error, "ENOENT")) return [];
|
|
2450
|
+
throw error;
|
|
2451
|
+
})).filter((entry) => entry.isDirectory()).map((entry) => resolve(appletsDir, entry.name)).filter((root) => existsSync(resolve(root, "applet.manifest.ts"))).sort();
|
|
2452
|
+
if (roots.length === 0) throw new Error(`No applets were found under ${appletsDir}.`);
|
|
2453
|
+
return roots;
|
|
2454
|
+
}
|
|
2455
|
+
async function gitChanged(topLevel, base, pathspecs, env) {
|
|
2456
|
+
try {
|
|
2457
|
+
await execFileAsync("git", [
|
|
2458
|
+
"diff",
|
|
2459
|
+
"--quiet",
|
|
2460
|
+
base,
|
|
2461
|
+
"HEAD",
|
|
2462
|
+
"--",
|
|
2463
|
+
...pathspecs
|
|
2464
|
+
], {
|
|
2465
|
+
cwd: topLevel,
|
|
2466
|
+
env
|
|
2467
|
+
});
|
|
2468
|
+
return false;
|
|
2469
|
+
} catch (error) {
|
|
2470
|
+
if (error instanceof Error && "code" in error && error.code === 1) return true;
|
|
2471
|
+
throw error;
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
async function hasCommit(topLevel, sha, env) {
|
|
2475
|
+
const git = (args) => execFileAsync("git", args, {
|
|
2476
|
+
cwd: topLevel,
|
|
2477
|
+
env
|
|
2478
|
+
});
|
|
2479
|
+
return git([
|
|
2480
|
+
"cat-file",
|
|
2481
|
+
"-e",
|
|
2482
|
+
`${sha}^{commit}`
|
|
2483
|
+
]).catch(() => git([
|
|
2484
|
+
"fetch",
|
|
2485
|
+
"--depth=1",
|
|
2486
|
+
"origin",
|
|
2487
|
+
sha
|
|
2488
|
+
])).then(() => true, () => false);
|
|
2489
|
+
}
|
|
2490
|
+
async function ciDeployDecisions(github, workspaceRoot, appletRoots, options) {
|
|
2491
|
+
const env = gitEnvironment();
|
|
2492
|
+
const [sources, { stdout: topLevelOutput }, directories] = await Promise.all([
|
|
2493
|
+
loadGitHubActionsSources(github, options),
|
|
2494
|
+
execFileAsync("git", ["rev-parse", "--show-toplevel"], {
|
|
2495
|
+
cwd: workspaceRoot,
|
|
2496
|
+
env
|
|
2497
|
+
}),
|
|
2498
|
+
Promise.all(appletRoots.map((appletRoot) => repositoryDirectory(appletRoot)))
|
|
2499
|
+
]);
|
|
2500
|
+
const topLevel = topLevelOutput.trim();
|
|
2501
|
+
const appletsDirectory = relative(topLevel, resolve(workspaceRoot, "applets")).split(sep).join("/");
|
|
2502
|
+
const baseByDirectory = new Map(sources.map((source) => [source.sourceDirectory, source.gitSha]));
|
|
2503
|
+
const bases = new Set(directories.flatMap((directory) => baseByDirectory.get(directory) ?? []));
|
|
2504
|
+
const sharedChanges = /* @__PURE__ */ new Map();
|
|
2505
|
+
for (const base of bases) sharedChanges.set(base, await hasCommit(topLevel, base, env) ? await gitChanged(topLevel, base, [`:(exclude)${appletsDirectory}`], env) : void 0);
|
|
2506
|
+
const decisions = await Promise.all(appletRoots.map(async (appletRoot, index) => {
|
|
2507
|
+
const directory = directories[index];
|
|
2508
|
+
const base = baseByDirectory.get(directory) ?? null;
|
|
2509
|
+
if (base === null) return [appletRoot, {
|
|
2510
|
+
deploy: true,
|
|
2511
|
+
reason: "no CI release to compare"
|
|
2512
|
+
}];
|
|
2513
|
+
const short = base.slice(0, 7);
|
|
2514
|
+
const sharedChanged = sharedChanges.get(base);
|
|
2515
|
+
if (sharedChanged === void 0) return [appletRoot, {
|
|
2516
|
+
deploy: true,
|
|
2517
|
+
reason: `commit ${short} is not in the repository`
|
|
2518
|
+
}];
|
|
2519
|
+
if (await gitChanged(topLevel, base, [directory], env)) return [appletRoot, {
|
|
2520
|
+
deploy: true,
|
|
2521
|
+
reason: `${directory} changed since ${short}`
|
|
2522
|
+
}];
|
|
2523
|
+
if (sharedChanged) return [appletRoot, {
|
|
2524
|
+
deploy: true,
|
|
2525
|
+
reason: `files outside ${appletsDirectory} changed since ${short}`
|
|
2526
|
+
}];
|
|
2527
|
+
return [appletRoot, {
|
|
2528
|
+
deploy: false,
|
|
2529
|
+
reason: `no changes since ${short}`
|
|
2530
|
+
}];
|
|
2531
|
+
}));
|
|
2532
|
+
return new Map(decisions);
|
|
2533
|
+
}
|
|
2534
|
+
async function runDeploy(options) {
|
|
2535
|
+
const root = process.cwd();
|
|
2536
|
+
const github = githubActionsContext(options.store?.env ?? process.env);
|
|
2537
|
+
if (existsSync(resolve(root, "applet.manifest.ts"))) {
|
|
2538
|
+
await deployApplet(root, options);
|
|
2539
|
+
return;
|
|
2540
|
+
}
|
|
2541
|
+
if (readWorkspaceConfig(root) === void 0) throw new Error("Run applet deploy in an applet folder or in the workspace root.");
|
|
2542
|
+
const appletRoots = await workspaceAppletRoots(root);
|
|
2543
|
+
const decisions = github === void 0 ? void 0 : await ciDeployDecisions(github, root, appletRoots, options);
|
|
2544
|
+
const selectedRoots = appletRoots.filter((appletRoot) => {
|
|
2545
|
+
const name = relative(root, appletRoot);
|
|
2546
|
+
const decision = decisions?.get(appletRoot);
|
|
2547
|
+
if (decision === void 0) console.log(`Deploying ${name}...`);
|
|
2548
|
+
else if (decision.deploy) console.log(`Deploying ${name}: ${decision.reason}.`);
|
|
2549
|
+
else {
|
|
2550
|
+
console.log(`Skipping ${name}: ${decision.reason}.`);
|
|
2551
|
+
return false;
|
|
2552
|
+
}
|
|
2553
|
+
return true;
|
|
2554
|
+
});
|
|
2555
|
+
if (selectedRoots.length === 0) return;
|
|
2556
|
+
if (options.dist === void 0) await prepareWorkspaceBuild(root, appletRoots);
|
|
2557
|
+
const interactive = github === void 0 && (options.stdinIsTTY ?? process.stdin.isTTY === true);
|
|
2558
|
+
const deploy = async (appletRoot) => {
|
|
2559
|
+
const logPrefix = `[${relative(root, appletRoot)}] `;
|
|
2560
|
+
if (options.dist === void 0) await buildPreparedApplet(appletRoot, ".hitch/dist", interactive ? "" : logPrefix);
|
|
2561
|
+
await deployApplet(appletRoot, {
|
|
2562
|
+
...options,
|
|
2563
|
+
dist: options.dist ?? ".hitch/dist"
|
|
2564
|
+
}, logPrefix);
|
|
2565
|
+
};
|
|
2566
|
+
if (interactive) {
|
|
2567
|
+
for (const appletRoot of selectedRoots) await deploy(appletRoot);
|
|
2568
|
+
return;
|
|
2569
|
+
}
|
|
2570
|
+
const failedApplets = (await Promise.allSettled(selectedRoots.map(deploy))).flatMap((result, index) => {
|
|
2571
|
+
if (result.status === "fulfilled") return [];
|
|
2572
|
+
const name = relative(root, selectedRoots[index]);
|
|
2573
|
+
console.error(`[${name}] ${result.reason instanceof Error ? result.reason.message : String(result.reason)}`);
|
|
2574
|
+
return [name];
|
|
2575
|
+
});
|
|
2576
|
+
if (failedApplets.length > 0) throw new WorkspaceDeployError(failedApplets);
|
|
2577
|
+
}
|
|
2415
2578
|
//#endregion
|
|
2416
2579
|
export { runDeploy };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { oc } from "@orpc/contract";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/hitch/email.ts
|
|
4
|
+
const emailTagKeySchema = z.string().regex(/^[A-Za-z][A-Za-z0-9_]{0,63}$/);
|
|
5
|
+
const emailTagsSchema = z.record(emailTagKeySchema, z.string().min(1).max(200)).refine((tags) => Object.keys(tags).length <= 10);
|
|
6
|
+
const createEmailPixelInputSchema = z.object({ tags: emailTagsSchema.optional() }).strict();
|
|
7
|
+
const createEmailPixelOutputSchema = z.object({
|
|
8
|
+
id: z.uuid(),
|
|
9
|
+
url: z.url()
|
|
10
|
+
});
|
|
11
|
+
const getEmailTrackerInputSchema = z.object({ id: z.uuid() });
|
|
12
|
+
const markEmailTrackerSentInputSchema = z.object({
|
|
13
|
+
id: z.uuid(),
|
|
14
|
+
sentAt: z.iso.datetime({ offset: true }).optional()
|
|
15
|
+
}).strict();
|
|
16
|
+
const markEmailTrackerSentBodySchema = z.object({ sentAt: z.iso.datetime({ offset: true }).optional() }).strict();
|
|
17
|
+
const emailTrackerSchema = z.object({
|
|
18
|
+
id: z.uuid(),
|
|
19
|
+
url: z.url(),
|
|
20
|
+
tags: emailTagsSchema,
|
|
21
|
+
createdAt: z.iso.datetime(),
|
|
22
|
+
sentAt: z.iso.datetime().nullable(),
|
|
23
|
+
firstHitAt: z.iso.datetime().nullable(),
|
|
24
|
+
lastHitAt: z.iso.datetime().nullable()
|
|
25
|
+
});
|
|
26
|
+
const listEmailTrackersInputSchema = z.object({
|
|
27
|
+
filter: emailTagsSchema.optional(),
|
|
28
|
+
sentAt: z.object({
|
|
29
|
+
$gte: z.iso.datetime({ offset: true }).optional(),
|
|
30
|
+
$lt: z.iso.datetime({ offset: true }).optional()
|
|
31
|
+
}).strict().optional(),
|
|
32
|
+
cursor: z.uuid().optional(),
|
|
33
|
+
limit: z.number().int().min(1).max(200).optional()
|
|
34
|
+
}).strict();
|
|
35
|
+
const listEmailTrackersOutputSchema = z.object({
|
|
36
|
+
trackers: z.array(emailTrackerSchema),
|
|
37
|
+
nextCursor: z.uuid().nullable()
|
|
38
|
+
});
|
|
39
|
+
function emailContract(appletCode) {
|
|
40
|
+
const path = `/v1/applets/${encodeURIComponent(appletCode)}/email/tracking`;
|
|
41
|
+
return { tracking: {
|
|
42
|
+
createPixel: oc.route({
|
|
43
|
+
method: "POST",
|
|
44
|
+
path: `${path}/pixels`,
|
|
45
|
+
successStatus: 201
|
|
46
|
+
}).input(createEmailPixelInputSchema.optional()).output(createEmailPixelOutputSchema),
|
|
47
|
+
markSent: oc.route({
|
|
48
|
+
method: "POST",
|
|
49
|
+
path: `${path}/{id}/sent`
|
|
50
|
+
}).input(markEmailTrackerSentInputSchema).output(emailTrackerSchema),
|
|
51
|
+
get: oc.route({
|
|
52
|
+
method: "GET",
|
|
53
|
+
path: `${path}/{id}`
|
|
54
|
+
}).input(getEmailTrackerInputSchema).output(emailTrackerSchema),
|
|
55
|
+
list: oc.route({
|
|
56
|
+
method: "POST",
|
|
57
|
+
path: `${path}/list`
|
|
58
|
+
}).input(listEmailTrackersInputSchema.optional()).output(listEmailTrackersOutputSchema)
|
|
59
|
+
} };
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
export { emailTagsSchema as a, listEmailTrackersInputSchema as c, markEmailTrackerSentInputSchema as d, emailTagKeySchema as i, listEmailTrackersOutputSchema as l, createEmailPixelOutputSchema as n, emailTrackerSchema as o, emailContract as r, getEmailTrackerInputSchema as s, createEmailPixelInputSchema as t, markEmailTrackerSentBodySchema as u };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { c as organizationInstanceUrl, d as resolveInstanceUrl, n as fetchWithOrgAuth } from "./workspace-XVtM12ZC.js";
|
|
2
|
+
import { i as loadGitHubActionsBuildInputs, n as githubActionsContext } from "./ci-5qWl7lT3.js";
|
|
3
|
+
import { t as loadAppletConfig } from "./load-config-BYSxDVAl.js";
|
|
4
|
+
import { r as prepareAppletProject } from "./project-D6XHllmC.js";
|
|
4
5
|
import { createRequire } from "node:module";
|
|
5
6
|
import { existsSync } from "node:fs";
|
|
6
7
|
import { access, mkdir, mkdtemp, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
|
|
@@ -54,7 +55,7 @@ export default createWorkerEntry(server, { code: ${JSON.stringify(appletCode)} }
|
|
|
54
55
|
}
|
|
55
56
|
//#endregion
|
|
56
57
|
//#region src/typegen/operations.ts
|
|
57
|
-
const RECORD_ROUTE = /^\/v1\/([^/]+)(?:\/(\{id\}
|
|
58
|
+
const RECORD_ROUTE = /^\/v1\/([^/]+)(?:\/(\{id\}(?:\/history)?|query|update|aggregate))?$/;
|
|
58
59
|
const COMPONENT_REF_PREFIX$1 = "#/components/schemas/";
|
|
59
60
|
const BATCH_PATH = "/v1/batch";
|
|
60
61
|
const BATCH_ITEM_PATH = "paths./v1/batch.post.requestBody.content.application/json.schema.properties.requests.items";
|
|
@@ -63,6 +64,7 @@ const VERBS = {
|
|
|
63
64
|
"get:": "list",
|
|
64
65
|
"post:": "create",
|
|
65
66
|
"get:{id}": "get",
|
|
67
|
+
"get:{id}/history": "history",
|
|
66
68
|
"patch:{id}": "update",
|
|
67
69
|
"delete:{id}": "delete",
|
|
68
70
|
"post:query": "query",
|
|
@@ -73,6 +75,7 @@ const COMPONENT_RESPONSE_STATUS = {
|
|
|
73
75
|
list: "200",
|
|
74
76
|
create: "201",
|
|
75
77
|
get: "200",
|
|
78
|
+
history: "200",
|
|
76
79
|
update: "200",
|
|
77
80
|
query: "200"
|
|
78
81
|
};
|
|
@@ -148,10 +151,13 @@ function prepareBatchOperation(operation, manifest) {
|
|
|
148
151
|
if (manifest === void 0) return true;
|
|
149
152
|
const object = manifest.objects.find(({ code }) => code === objectCode);
|
|
150
153
|
if (object === void 0) return false;
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
154
|
+
switch (verb) {
|
|
155
|
+
case "create": return object.canCreate;
|
|
156
|
+
case "delete": return object.canDelete;
|
|
157
|
+
case "update":
|
|
158
|
+
case "updateWhere": return object.readWrite.length > 0;
|
|
159
|
+
default: return true;
|
|
160
|
+
}
|
|
155
161
|
});
|
|
156
162
|
if (entries.length === 0) return void 0;
|
|
157
163
|
items.oneOf = entries;
|
|
@@ -165,7 +171,7 @@ function prepareRecordSpec(spec, manifest) {
|
|
|
165
171
|
const prepared = structuredClone(spec);
|
|
166
172
|
prepared.paths = Object.fromEntries(Object.entries(prepared.paths).flatMap(([path, pathItem]) => {
|
|
167
173
|
if (path === BATCH_PATH) {
|
|
168
|
-
if (!isObject$2(pathItem)
|
|
174
|
+
if (!isObject$2(pathItem)) return [];
|
|
169
175
|
const operation = prepareBatchOperation(pathItem.post, manifest);
|
|
170
176
|
return operation === void 0 ? [] : [[path, { post: operation }]];
|
|
171
177
|
}
|
|
@@ -273,7 +279,7 @@ ${Object.entries(catalog).map(([objectCode, attributeCodes]) => ` ${JSON.string
|
|
|
273
279
|
};`;
|
|
274
280
|
return `// Generated by @hitch42/applet — do not edit.
|
|
275
281
|
import type manifest from "./manifest.gen.ts";
|
|
276
|
-
${options.hasServer ? "import type server from \"../src/server/main.ts\";\nimport type * as serverModule from \"../src/server/main.ts\";\n" : ""}${options.hasClient ? "import type { Route as indexRoute } from \"../src/client/routes/index.tsx\";\n" : ""}${options.hasServer ? "import type { AnyRouter } from \"@orpc/server\";\n" : ""}${options.hasClient ? "import type { AnyRoute } from \"@tanstack/react-router\";\n" : ""}${options.hasServer ? "import type { WorkflowDefinition } from \"@hitch42/applet/server\";\n" : ""}
|
|
282
|
+
${options.hasServer ? "import type server from \"../src/server/main.ts\";\nimport type * as serverModule from \"../src/server/main.ts\";\n" : ""}${options.hasClient ? "import type { Route as indexRoute } from \"../src/client/routes/index.tsx\";\n" : ""}${options.hasServer ? "import type { AnyRouter } from \"@orpc/server\";\n" : ""}${options.hasClient ? "import type { AnyRoute } from \"@tanstack/react-router\";\n" : ""}${options.hasServer ? "import type { WorkflowDefinition } from \"@hitch42/applet/server\";\nimport type { TelephonyMessageReceivedEvent } from \"@hitch42/applet\";\n" : ""}
|
|
277
283
|
${catalogType}
|
|
278
284
|
|
|
279
285
|
type Expect<T extends true> = T;
|
|
@@ -286,6 +292,7 @@ type _SrcServerMainMustDefaultExportRouterAndValidWorkflows = Expect<
|
|
|
286
292
|
typeof server extends {
|
|
287
293
|
router: AnyRouter;
|
|
288
294
|
workflows?: Record<string, WorkflowDefinition<any, any>>;
|
|
295
|
+
telephony?: { onMessageReceived?: (event: TelephonyMessageReceivedEvent) => void | Promise<void> };
|
|
289
296
|
}
|
|
290
297
|
? true
|
|
291
298
|
: false
|
|
@@ -351,31 +358,35 @@ async function responseJson(response) {
|
|
|
351
358
|
throw new Error("Failed to parse the organization's OpenAPI spec.", { cause: error });
|
|
352
359
|
}
|
|
353
360
|
}
|
|
354
|
-
async function loadAuthenticatedSpec(
|
|
355
|
-
const
|
|
356
|
-
if (
|
|
357
|
-
return JSON.parse(await readFile(ciPath, "utf8"));
|
|
358
|
-
} catch (error) {
|
|
359
|
-
throw new Error(`Failed to read the CI OpenAPI input from ${ciPath}.`, { cause: error });
|
|
360
|
-
}
|
|
361
|
+
async function loadAuthenticatedSpec(manifest, options) {
|
|
362
|
+
const github = githubActionsContext(options.store?.env ?? process.env);
|
|
363
|
+
if (github !== void 0) return (await loadGitHubActionsBuildInputs(github, options, manifest.requiresChangeset)).openapi;
|
|
361
364
|
const fetcher = options.fetch ?? globalThis.fetch;
|
|
362
|
-
const response = await fetchWithOrgAuth(
|
|
365
|
+
const response = await fetchWithOrgAuth(manifest.org, options, (instanceUrl, accessToken) => fetcher(`${instanceUrl}/api/v1/openapi.json${manifest.requiresChangeset === void 0 ? "" : `?changeset_id=${encodeURIComponent(manifest.requiresChangeset)}`}`, { headers: { Authorization: `Bearer ${accessToken}` } }));
|
|
363
366
|
if (!response.ok) throw new Error(`Failed to load the organization's OpenAPI spec (${response.status}).`);
|
|
364
367
|
return responseJson(response);
|
|
365
368
|
}
|
|
366
369
|
async function loadThemeStylesheet(orgCode, options) {
|
|
367
|
-
const ciPath = (options.store?.env ?? process.env).HITCH_CI_THEME_PATH;
|
|
368
|
-
if (ciPath !== void 0) try {
|
|
369
|
-
return await readFile(ciPath, "utf8");
|
|
370
|
-
} catch (error) {
|
|
371
|
-
throw new Error(`Failed to read the CI theme input from ${ciPath}.`, { cause: error });
|
|
372
|
-
}
|
|
373
370
|
const fetcher = options.fetch ?? globalThis.fetch;
|
|
374
371
|
const instanceUrl = await resolveInstanceUrl(void 0, options.store);
|
|
375
372
|
const response = await fetcher(`${organizationInstanceUrl(instanceUrl, orgCode)}/theme.css`);
|
|
376
373
|
if (!response.ok) throw new Error(`Failed to load the organization's theme (${response.status}).`);
|
|
377
374
|
return response.text();
|
|
378
375
|
}
|
|
376
|
+
async function loadBuildInputs(manifest, options) {
|
|
377
|
+
const github = githubActionsContext(options.store?.env ?? process.env);
|
|
378
|
+
if (github !== void 0) {
|
|
379
|
+
const inputs = await loadGitHubActionsBuildInputs(github, options, manifest.requiresChangeset);
|
|
380
|
+
return {
|
|
381
|
+
spec: inputs.openapi,
|
|
382
|
+
theme: inputs.theme
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
spec: await loadAuthenticatedSpec(manifest, options),
|
|
387
|
+
theme: await loadThemeStylesheet(manifest.org, options)
|
|
388
|
+
};
|
|
389
|
+
}
|
|
379
390
|
async function generateContract(spec, outDir) {
|
|
380
391
|
await mkdir(outDir, { recursive: true });
|
|
381
392
|
const paths = spec !== null && typeof spec === "object" && "paths" in spec ? spec.paths : void 0;
|
|
@@ -415,16 +426,16 @@ async function writeContractFiles(outDir, manifest, spec) {
|
|
|
415
426
|
await writeManifestGen(outDir, manifest);
|
|
416
427
|
await writeRegisterDeclaration(outDir, catalog);
|
|
417
428
|
await writeFile(resolve(outDir, "index.ts"), `// Generated by applet generate — do not edit.
|
|
418
|
-
import { filesContract as files, meContract as me, pruneContract, registerHitchContract, teamsContract as teams } from "@hitch42/applet";
|
|
429
|
+
import { emailContract, filesContract as files, meContract as me, pruneContract, registerHitchContract, teamsContract as teams, telephonyContract as telephony } from "@hitch42/applet";
|
|
419
430
|
import manifest from "./manifest.gen.ts";
|
|
420
431
|
|
|
421
432
|
${Object.hasOwn(preparedSpec.paths, "/v1/batch") ? `import { contract as generated } from "./orpc.gen.ts";
|
|
422
433
|
|
|
423
434
|
const { batch, ...records } = generated;
|
|
424
435
|
|
|
425
|
-
export const contract = { records, batch, files, me, teams } as const;` : `import { contract as records } from "./orpc.gen.ts";
|
|
436
|
+
export const contract = { records, batch, email: emailContract(manifest.code), files, me, teams, telephony } as const;` : `import { contract as records } from "./orpc.gen.ts";
|
|
426
437
|
|
|
427
|
-
export const contract = { records, files, me, teams } as const;`}
|
|
438
|
+
export const contract = { records, email: emailContract(manifest.code), files, me, teams, telephony } as const;`}
|
|
428
439
|
registerHitchContract(pruneContract(contract, manifest));
|
|
429
440
|
|
|
430
441
|
declare module "@hitch42/applet" {
|
|
@@ -437,15 +448,14 @@ export type * from "./zod.gen.ts";
|
|
|
437
448
|
`);
|
|
438
449
|
}
|
|
439
450
|
async function syncContract(outDir, manifest, options = {}) {
|
|
440
|
-
await writeContractFiles(outDir, manifest, await loadAuthenticatedSpec(manifest
|
|
451
|
+
await writeContractFiles(outDir, manifest, await loadAuthenticatedSpec(manifest, options));
|
|
441
452
|
}
|
|
442
453
|
async function runGenerate(options) {
|
|
443
454
|
await prepareAppletProject();
|
|
444
455
|
const root = process.cwd();
|
|
445
456
|
const outDir = resolve("./.hitch");
|
|
446
457
|
const manifest = await loadAppletConfig(process.cwd());
|
|
447
|
-
const spec = await
|
|
448
|
-
const themeStylesheet = await loadThemeStylesheet(manifest.org, options);
|
|
458
|
+
const { spec, theme: themeStylesheet } = await loadBuildInputs(manifest, options);
|
|
449
459
|
const stagedDir = await mkdtemp(resolve("./.hitch.tmp-"));
|
|
450
460
|
const backupDir = `${stagedDir}.backup`;
|
|
451
461
|
let staged = true;
|