@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.
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-Ch3pALLA.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-DP2Bg6nt.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-BuszZ9-U.js → generate-CBPnHoqX.js} +40 -30
  13. package/dist/index.d.ts +122 -11
  14. package/dist/index.js +141 -26
  15. package/dist/{init-BjWWDqqX.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-MN3mFxeE.js → project-D6XHllmC.js} +26 -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 +458 -27
  35. package/dist/workflow-engine-MogejNfF.js +117 -0
  36. package/dist/{auth-DaVK0k5R.js → workspace-XVtM12ZC.js} +44 -4
  37. package/package.json +6 -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 +1 -7
  53. package/dist/build-BqoQ-uNz.js +0 -2
  54. package/dist/generate-CRVA0Ll5.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-Ch3pALLA.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-MN3mFxeE.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,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(orgCode, options) {
355
- const ciPath = (options.store?.env ?? process.env).HITCH_CI_OPENAPI_PATH;
356
- if (ciPath !== void 0) try {
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(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}` } }));
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.org, options));
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 loadAuthenticatedSpec(manifest.org, options);
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;