@hitch42/applet 0.1.0-beta.2 → 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.
Files changed (55) hide show
  1. package/dist/_internal/server.d.ts +14 -1
  2. package/dist/_internal/server.js +23 -74
  3. package/dist/applet.js +53 -29
  4. package/dist/build-BfbTbt_r.js +2 -0
  5. package/dist/{build-BRkmrUcj.js → build-CAb26kMY.js} +30 -14
  6. package/dist/ci-5qWl7lT3.js +68 -0
  7. package/dist/client.d.ts +3 -3
  8. package/dist/{contract-DMrpbaLJ.d.ts → contract-rP_eV9PK.d.ts} +1 -1
  9. package/dist/{deploy-DKB-Tef7.js → deploy-D_OFJ6-Q.js} +193 -30
  10. package/dist/email-DBTVhNB_.js +62 -0
  11. package/dist/generate-BgepN4-b.js +2 -0
  12. package/dist/{generate-BFmIzHaR.js → generate-CBPnHoqX.js} +40 -18
  13. package/dist/index.d.ts +122 -11
  14. package/dist/index.js +141 -26
  15. package/dist/{init-CQRsnd3i.js → init-tfzwuYnk.js} +12 -8
  16. package/dist/load-config-BYSxDVAl.js +27 -0
  17. package/dist/login-C7RKmmTe.js +12 -0
  18. package/dist/{logs-m00pzr_O.js → logs-DgjgAxj2.js} +3 -3
  19. package/dist/{manifest-DyoJ1MIL.js → manifest-Bp87Uz0a.js} +6 -0
  20. package/dist/{manifest-BQCAxATS.d.ts → manifest-DD_SGkS6.d.ts} +5 -0
  21. package/dist/me-BW7Paary.js +32 -0
  22. package/dist/{project-CEmDnlKa.js → project-D6XHllmC.js} +32 -20
  23. package/dist/prompt-ChS-grYO.js +29 -0
  24. package/dist/react.d.ts +1 -1
  25. package/dist/react.js +40 -42
  26. package/dist/schedules-dhU1yyFr.js +42 -0
  27. package/dist/server.d.ts +3 -3
  28. package/dist/telephony-protocol-D-nf3Gca.d.ts +76 -0
  29. package/dist/telephony.d.ts +77 -0
  30. package/dist/telephony.js +607 -0
  31. package/dist/{upload-YbRKEqR6.d.ts → upload-DKZzz5kW.d.ts} +6 -6
  32. package/dist/{variables-CP7Ce3Np.js → variables-Bwnr7oxf.js} +3 -28
  33. package/dist/vite.d.ts +1 -1
  34. package/dist/vite.js +464 -30
  35. package/dist/workflow-engine-MogejNfF.js +117 -0
  36. package/dist/{auth-DaVK0k5R.js → workspace-XVtM12ZC.js} +44 -4
  37. package/package.json +7 -1
  38. package/scaffold/.agents/skills/hitch-applets/SKILL.md +34 -15
  39. package/scaffold/.agents/skills/hitch-applets/references/email-tracking.md +49 -0
  40. package/scaffold/.agents/skills/hitch-applets/references/files.md +3 -3
  41. package/scaffold/.agents/skills/hitch-applets/references/manifest.md +21 -2
  42. package/scaffold/.agents/skills/hitch-applets/references/performance.md +74 -0
  43. package/scaffold/.agents/skills/hitch-applets/references/records.md +30 -8
  44. package/scaffold/.agents/skills/hitch-applets/references/schedules.md +7 -2
  45. package/scaffold/.agents/skills/hitch-applets/references/server.md +0 -3
  46. package/scaffold/.agents/skills/hitch-applets/references/workflows.md +11 -3
  47. package/scaffold/.agents/skills/hitch-cli/SKILL.md +129 -0
  48. package/scaffold/.agents/skills/hitch-data/SKILL.md +51 -0
  49. package/scaffold/.agents/skills/hitch-data/references/attributes.md +121 -0
  50. package/scaffold/.agents/skills/hitch-data/references/limits.md +64 -0
  51. package/scaffold/.agents/skills/hitch-theory/SKILL.md +150 -0
  52. package/scaffold/.github/workflows/hitch.yml +15 -0
  53. package/dist/build-CMluoV6q.js +0 -2
  54. package/dist/generate-CefnB-bp.js +0 -2
  55. package/dist/load-config-BnxocLAQ.js +0 -56
@@ -1,6 +1,8 @@
1
- import { t as fetchWithOrgAuth } from "./auth-DaVK0k5R.js";
2
- import { t as buildApplet } from "./build-BRkmrUcj.js";
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
- async function readGitInfo() {
2359
- const env = Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith("GIT_")));
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: process.cwd(),
2373
+ cwd: appletRoot,
2363
2374
  env
2364
2375
  }), execFileAsync("git", ["rev-parse", "HEAD"], {
2365
- cwd: process.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 deployError(response) {
2377
- let details = "";
2378
- try {
2379
- const body = await response.json();
2380
- if (body !== null && typeof body === "object") details = ["code" in body && typeof body.code === "string" ? body.code : void 0, "message" in body && typeof body.message === "string" ? body.message : void 0].filter((value) => value !== void 0).join(": ");
2381
- } catch {
2382
- details = "";
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
- async function runDeploy(options) {
2387
- if (options.dist === void 0) await buildApplet(options.changesetId);
2388
- const distDir = resolve(options.dist ?? ".hitch/dist");
2389
- const manifest = await readManifest(distDir);
2390
- const [bundle, git] = await Promise.all([createBundle(distDir), readGitInfo()]);
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 response = await fetchWithOrgAuth(manifest.org, options, (instanceUrl, accessToken) => {
2393
- const body = new FormData();
2394
- body.set("manifest", manifest.raw);
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
- if (!response.ok) throw await deployError(response);
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(`Deployed release ${result.data.id} (${result.promoted ? "promoted" : "not promoted"}).`);
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(`Warning: ${warning.message}`);
2410
- console.warn(`Changeset ${result.changeset_id} cannot apply until the applet and the schema agree.`);
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(`Warning: declared variables not set: ${result.unset_variables.join(", ")}. Set them with applet variables set.`);
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 };
@@ -0,0 +1,2 @@
1
+ import { n as runGenerate } from "./generate-CBPnHoqX.js";
2
+ export { runGenerate };
@@ -1,6 +1,7 @@
1
- import { i as resolveInstanceUrl, n as organizationInstanceUrl, t as fetchWithOrgAuth } from "./auth-DaVK0k5R.js";
2
- import { t as loadAppletConfig } from "./load-config-BnxocLAQ.js";
3
- import { r as prepareAppletProject } from "./project-CEmDnlKa.js";
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\}|query|update|aggregate))?$/;
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
- if (verb === "create") return object.canCreate;
152
- if (verb === "delete") return object.canDelete;
153
- if (verb === "update" || verb === "updateWhere") return object.readWrite.length > 0;
154
- return true;
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) || pathItem.post === void 0) return [];
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,9 +358,11 @@ 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(orgCode, options) {
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;
355
364
  const fetcher = options.fetch ?? globalThis.fetch;
356
- const response = await fetchWithOrgAuth(orgCode, options, (instanceUrl, accessToken) => fetcher(`${instanceUrl}/api/v1/openapi.json${options.changesetId === void 0 ? "" : `?changeset_id=${encodeURIComponent(options.changesetId)}`}`, { headers: { Authorization: `Bearer ${accessToken}` } }));
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}` } }));
357
366
  if (!response.ok) throw new Error(`Failed to load the organization's OpenAPI spec (${response.status}).`);
358
367
  return responseJson(response);
359
368
  }
@@ -364,6 +373,20 @@ async function loadThemeStylesheet(orgCode, options) {
364
373
  if (!response.ok) throw new Error(`Failed to load the organization's theme (${response.status}).`);
365
374
  return response.text();
366
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
+ }
367
390
  async function generateContract(spec, outDir) {
368
391
  await mkdir(outDir, { recursive: true });
369
392
  const paths = spec !== null && typeof spec === "object" && "paths" in spec ? spec.paths : void 0;
@@ -403,16 +426,16 @@ async function writeContractFiles(outDir, manifest, spec) {
403
426
  await writeManifestGen(outDir, manifest);
404
427
  await writeRegisterDeclaration(outDir, catalog);
405
428
  await writeFile(resolve(outDir, "index.ts"), `// Generated by applet generate — do not edit.
406
- 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";
407
430
  import manifest from "./manifest.gen.ts";
408
431
 
409
432
  ${Object.hasOwn(preparedSpec.paths, "/v1/batch") ? `import { contract as generated } from "./orpc.gen.ts";
410
433
 
411
434
  const { batch, ...records } = generated;
412
435
 
413
- 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";
414
437
 
415
- export const contract = { records, files, me, teams } as const;`}
438
+ export const contract = { records, email: emailContract(manifest.code), files, me, teams, telephony } as const;`}
416
439
  registerHitchContract(pruneContract(contract, manifest));
417
440
 
418
441
  declare module "@hitch42/applet" {
@@ -425,15 +448,14 @@ export type * from "./zod.gen.ts";
425
448
  `);
426
449
  }
427
450
  async function syncContract(outDir, manifest, options = {}) {
428
- await writeContractFiles(outDir, manifest, await loadAuthenticatedSpec(manifest.org, options));
451
+ await writeContractFiles(outDir, manifest, await loadAuthenticatedSpec(manifest, options));
429
452
  }
430
453
  async function runGenerate(options) {
431
454
  await prepareAppletProject();
432
455
  const root = process.cwd();
433
456
  const outDir = resolve("./.hitch");
434
457
  const manifest = await loadAppletConfig(process.cwd());
435
- const spec = await loadAuthenticatedSpec(manifest.org, options);
436
- const themeStylesheet = await loadThemeStylesheet(manifest.org, options);
458
+ const { spec, theme: themeStylesheet } = await loadBuildInputs(manifest, options);
437
459
  const stagedDir = await mkdtemp(resolve("./.hitch.tmp-"));
438
460
  const backupDir = `${stagedDir}.backup`;
439
461
  let staged = true;
package/dist/index.d.ts CHANGED
@@ -1,12 +1,14 @@
1
- import { _ as RegisteredWorkflows, a as AppletSourceDefinition, c as appletManifestSchema, d as RegisteredCatalog, f as RegisteredContract, g as RegisteredVariables, h as RegisteredVariableKeys, i as AppletPackages, l as appletSourceDefinitionSchema, m as RegisteredRouter, n as AppletManifest, o as RegisteredAppletSourceDefinition, p as RegisteredManifest, r as AppletManifestObject, s as appletDefinitionSchema, t as AppletDefinition, u as HitchRegister, v as registerHitchContract } from "./manifest-BQCAxATS.js";
2
- import { n as pruneContract, t as NarrowContract } from "./contract-DMrpbaLJ.js";
3
- import { a as UploadedHitchFile, c as createFileOutputSchema, d as getFileOutputSchema, i as UploadProgress, l as fileSchema, n as FilesUpload, r as UploadFileOptions, s as uploadFile, t as FilesClient, u as filesContract } from "./upload-YbRKEqR6.js";
1
+ import { _ as RegisteredWorkflows, a as AppletSourceDefinition, c as appletManifestSchema, d as RegisteredCatalog, f as RegisteredContract, g as RegisteredVariables, h as RegisteredVariableKeys, i as AppletPackages, l as appletSourceDefinitionSchema, m as RegisteredRouter, n as AppletManifest, o as RegisteredAppletSourceDefinition, p as RegisteredManifest, r as AppletManifestObject, s as appletDefinitionSchema, t as AppletDefinition, u as HitchRegister, v as registerHitchContract } from "./manifest-DD_SGkS6.js";
2
+ import { n as pruneContract, t as NarrowContract } from "./contract-rP_eV9PK.js";
3
+ import { a as UploadedHitchFile, c as createFileOutputSchema, d as getFileOutputSchema, i as UploadProgress, l as fileSchema, n as FilesUpload, r as UploadFileOptions, s as uploadFile, t as FilesClient, u as filesContract } from "./upload-DKZzz5kW.js";
4
+ import { a as createEmailPixelOutputSchema, c as emailTagsSchema, d as listEmailTrackersInputSchema, f as listEmailTrackersOutputSchema, i as createEmailPixelInputSchema, l as emailTrackerSchema, m as markEmailTrackerSentInputSchema, n as EmailTracker, o as emailContract, r as ListEmailTrackersOutput, s as emailTagKeySchema, t as TelephonyMessageReceivedEvent, u as getEmailTrackerInputSchema } from "./telephony-protocol-D-nf3Gca.js";
4
5
  import { ContractProcedure, Meta } from "@orpc/contract";
5
6
  import { z } from "zod";
6
- //#region src/workspace.d.ts
7
+ //#region ../shared/auth/src/workspace.d.mts
7
8
  interface HitchConfig {
8
9
  sdk: string;
9
10
  org: string;
11
+ githubWorkflow?: boolean;
10
12
  }
11
13
  //#endregion
12
14
  //#region src/hitch/me.d.ts
@@ -33,12 +35,121 @@ type HitchMe = {
33
35
  } | null;
34
36
  multiOrg: boolean;
35
37
  };
36
- declare const meSchema: z.ZodType<HitchMe>;
38
+ declare const meSchema: z.ZodType<HitchMe, HitchMe>;
37
39
  type MeContract = {
38
- get: ContractProcedure<z.ZodType<void>, z.ZodType<HitchMe>, Record<never, never>, Meta>;
40
+ get: ContractProcedure<z.ZodType<void, void>, z.ZodType<HitchMe, HitchMe>, Record<never, never>, Meta>;
39
41
  };
40
42
  declare const meContract: MeContract;
41
43
  //#endregion
44
+ //#region src/hitch/telephony.d.ts
45
+ type HitchPhoneNumber = {
46
+ id: string;
47
+ e164: string;
48
+ label: string;
49
+ enabled: boolean;
50
+ canRead: boolean;
51
+ canSend: boolean;
52
+ canCall: boolean;
53
+ voiceEnabled: boolean;
54
+ };
55
+ type HitchCall = {
56
+ id: string;
57
+ numberId: string;
58
+ direction: "inbound" | "outbound";
59
+ status: "starting" | "ringing" | "active" | "ended" | "missed" | "failed";
60
+ from: string;
61
+ to: string;
62
+ userId: string | null;
63
+ answeredAt: string | null;
64
+ endedAt: string | null;
65
+ durationSeconds: number | null;
66
+ voicemailSeconds: number | null;
67
+ hangupCause: string | null;
68
+ record: boolean;
69
+ hasRecording: boolean;
70
+ transcriptStatus: "pending" | "done" | "failed" | null;
71
+ transcript: {
72
+ segments: {
73
+ side: "member" | "customer";
74
+ speaker: number;
75
+ start: number;
76
+ end: number;
77
+ text: string;
78
+ }[];
79
+ } | null;
80
+ createdAt: string;
81
+ };
82
+ type ListHitchCallsInput = ListHitchMessagesInput & {
83
+ status?: HitchCall["status"];
84
+ };
85
+ type HitchMessage = {
86
+ id: string;
87
+ numberId: string;
88
+ direction: "inbound" | "outbound";
89
+ status: "pending" | "unknown" | "accepted" | "sent" | "delivered" | "unconfirmed" | "failed" | "received";
90
+ from: string;
91
+ to: string;
92
+ body: string | null;
93
+ redacted: boolean;
94
+ userId: string | null;
95
+ appletId: string | null;
96
+ requestId: string | null;
97
+ errorCode: string | null;
98
+ errorDetail: string | null;
99
+ statusAt: string | null;
100
+ createdAt: string;
101
+ };
102
+ type ListHitchMessagesInput = {
103
+ numberId?: string;
104
+ userId?: string;
105
+ direction?: "inbound" | "outbound";
106
+ since?: string;
107
+ until?: string;
108
+ cursor?: string;
109
+ limit?: number;
110
+ };
111
+ type SendHitchMessageInput = {
112
+ numberId: string;
113
+ to: string;
114
+ body: string;
115
+ requestId?: string;
116
+ };
117
+ type MessageIdInput = {
118
+ id: string;
119
+ };
120
+ type NumbersOutput = {
121
+ data: HitchPhoneNumber[];
122
+ };
123
+ type MessagesOutput = {
124
+ data: HitchMessage[];
125
+ nextCursor: string | null;
126
+ };
127
+ type CallsOutput = {
128
+ data: HitchCall[];
129
+ nextCursor: string | null;
130
+ };
131
+ declare const hitchPhoneNumberSchema: z.ZodType<HitchPhoneNumber, HitchPhoneNumber>;
132
+ declare const hitchMessageSchema: z.ZodType<HitchMessage, HitchMessage>;
133
+ declare const listHitchNumbersOutputSchema: z.ZodType<NumbersOutput, NumbersOutput>;
134
+ declare const listHitchMessagesOutputSchema: z.ZodType<MessagesOutput, MessagesOutput>;
135
+ declare const listHitchMessagesInputSchema: z.ZodType<ListHitchMessagesInput, ListHitchMessagesInput>;
136
+ declare const sendHitchMessageInputSchema: z.ZodType<SendHitchMessageInput, SendHitchMessageInput>;
137
+ type TelephonyContract = {
138
+ calls: {
139
+ get: ContractProcedure<z.ZodType<MessageIdInput, MessageIdInput>, z.ZodType<HitchCall, HitchCall>, Record<never, never>, Meta>;
140
+ list: ContractProcedure<z.ZodType<ListHitchCallsInput, ListHitchCallsInput>, z.ZodType<CallsOutput, CallsOutput>, Record<never, never>, Meta>;
141
+ };
142
+ numbers: {
143
+ list: ContractProcedure<z.ZodType<void, void>, z.ZodType<NumbersOutput, NumbersOutput>, Record<never, never>, Meta>;
144
+ };
145
+ messages: {
146
+ get: ContractProcedure<z.ZodType<MessageIdInput, MessageIdInput>, z.ZodType<HitchMessage, HitchMessage>, Record<never, never>, Meta>;
147
+ list: ContractProcedure<z.ZodType<ListHitchMessagesInput, ListHitchMessagesInput>, z.ZodType<MessagesOutput, MessagesOutput>, Record<never, never>, Meta>;
148
+ send: ContractProcedure<z.ZodType<SendHitchMessageInput, SendHitchMessageInput>, z.ZodType<HitchMessage, HitchMessage>, Record<never, never>, Meta>;
149
+ };
150
+ };
151
+ declare const telephonyContract: TelephonyContract;
152
+ //#endregion
42
153
  //#region src/hitch/teams.d.ts
43
154
  type HitchTeam = {
44
155
  id: string;
@@ -61,14 +172,14 @@ type TeamsOutput = {
61
172
  type TeamMembersOutput = {
62
173
  data: HitchTeamMember[];
63
174
  };
64
- declare const teamSchema: z.ZodType<HitchTeam>;
65
- declare const teamMemberSchema: z.ZodType<HitchTeamMember>;
175
+ declare const teamSchema: z.ZodType<HitchTeam, HitchTeam>;
176
+ declare const teamMemberSchema: z.ZodType<HitchTeamMember, HitchTeamMember>;
66
177
  type TeamsContract = {
67
- list: ContractProcedure<z.ZodType<void>, z.ZodType<TeamsOutput>, Record<never, never>, Meta>;
178
+ list: ContractProcedure<z.ZodType<void, void>, z.ZodType<TeamsOutput, TeamsOutput>, Record<never, never>, Meta>;
68
179
  members: {
69
- list: ContractProcedure<z.ZodType<TeamIdInput>, z.ZodType<TeamMembersOutput>, Record<never, never>, Meta>;
180
+ list: ContractProcedure<z.ZodType<TeamIdInput, TeamIdInput>, z.ZodType<TeamMembersOutput, TeamMembersOutput>, Record<never, never>, Meta>;
70
181
  };
71
182
  };
72
183
  declare const teamsContract: TeamsContract;
73
184
  //#endregion
74
- export { type AppletDefinition, type AppletManifest, type AppletManifestObject, type AppletPackages, type AppletSourceDefinition, type FilesClient, type FilesUpload, type HitchConfig, type HitchMe, type HitchRegister, type HitchTeam, type HitchTeamMember, type NarrowContract, type RegisteredAppletSourceDefinition, type RegisteredCatalog, type RegisteredContract, type RegisteredManifest, type RegisteredRouter, type RegisteredVariableKeys, type RegisteredVariables, type RegisteredWorkflows, type UploadFileOptions, type UploadProgress, type UploadedHitchFile, appletDefinitionSchema, appletManifestSchema, appletSourceDefinitionSchema, createFileOutputSchema, fileSchema, filesContract, getFileOutputSchema, meContract, meSchema, pruneContract, registerHitchContract, teamMemberSchema, teamSchema, teamsContract, uploadFile };
185
+ export { type AppletDefinition, type AppletManifest, type AppletManifestObject, type AppletPackages, type AppletSourceDefinition, type EmailTracker, type FilesClient, type FilesUpload, type HitchConfig, type HitchMe, type HitchMessage, type HitchPhoneNumber, type HitchRegister, type HitchTeam, type HitchTeamMember, type ListEmailTrackersOutput, type ListHitchMessagesInput, type NarrowContract, type RegisteredAppletSourceDefinition, type RegisteredCatalog, type RegisteredContract, type RegisteredManifest, type RegisteredRouter, type RegisteredVariableKeys, type RegisteredVariables, type RegisteredWorkflows, type SendHitchMessageInput, type TelephonyContract, type TelephonyMessageReceivedEvent, type UploadFileOptions, type UploadProgress, type UploadedHitchFile, appletDefinitionSchema, appletManifestSchema, appletSourceDefinitionSchema, createEmailPixelInputSchema, createEmailPixelOutputSchema, createFileOutputSchema, emailContract, emailTagKeySchema, emailTagsSchema, emailTrackerSchema, fileSchema, filesContract, getEmailTrackerInputSchema, getFileOutputSchema, hitchMessageSchema, hitchPhoneNumberSchema, listEmailTrackersInputSchema, listEmailTrackersOutputSchema, listHitchMessagesInputSchema, listHitchMessagesOutputSchema, listHitchNumbersOutputSchema, markEmailTrackerSentInputSchema, meContract, meSchema, pruneContract, registerHitchContract, sendHitchMessageInputSchema, teamMemberSchema, teamSchema, teamsContract, telephonyContract, uploadFile };