@hitch42/applet 0.1.1-beta.6 → 0.1.1-beta.8
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 +50 -29
- package/dist/build-CFCKKMn7.js +2 -0
- package/dist/{build-DIsKQQT0.js → build-DANx9Hgg.js} +28 -13
- package/dist/{ci-D0nC_Ho7.js → ci-5qWl7lT3.js} +20 -4
- package/dist/client.d.ts +2 -2
- package/dist/{contract-DMrpbaLJ.d.ts → contract-rP_eV9PK.d.ts} +1 -1
- package/dist/{deploy-0dlUvCt3.js → deploy-MQWLG2EA.js} +130 -15
- package/dist/email-DBTVhNB_.js +62 -0
- package/dist/{generate-C6eAL5eo.js → generate-BxYHdoAQ.js} +18 -17
- package/dist/generate-CQ07Hdpo.js +2 -0
- package/dist/index.d.ts +115 -4
- package/dist/index.js +141 -26
- package/dist/{init-tEMeys9M.js → init-jDEgYIXg.js} +5 -5
- 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-mW0vsN8A.js → project-CbYPtpPq.js} +16 -14
- package/dist/prompt-ChS-grYO.js +29 -0
- package/dist/schedules-dhU1yyFr.js +42 -0
- package/dist/server.d.ts +2 -2
- package/dist/telephony-protocol-DRgiYjqQ.d.ts +74 -0
- package/dist/telephony.d.ts +77 -0
- package/dist/telephony.js +607 -0
- 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 +32 -16
- 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 +1 -1
- package/scaffold/.agents/skills/hitch-applets/references/records.md +4 -3
- 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 +24 -22
- package/scaffold/.agents/skills/hitch-data/SKILL.md +2 -2
- package/scaffold/.agents/skills/hitch-data/references/limits.md +10 -10
- package/scaffold/.agents/skills/hitch-theory/SKILL.md +150 -0
- package/scaffold/.github/workflows/hitch.yml +0 -6
- package/dist/build-B70gkDi5.js +0 -2
- package/dist/generate-D5rSXXa-.js +0 -2
- package/dist/load-config-BnxocLAQ.js +0 -56
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { t as AnyWorkflowDefinition } from "../workflow-DurM6Fwx.js";
|
|
2
|
+
import { d as listEmailTrackersInputSchema, i as createEmailPixelInputSchema, p as markEmailTrackerSentBodySchema, t as TelephonyMessageReceivedEvent } from "../telephony-protocol-DRgiYjqQ.js";
|
|
2
3
|
import { z } from "zod";
|
|
3
4
|
//#region src/server/entry.d.ts
|
|
4
5
|
declare function createWorkerEntry(server: {
|
|
5
6
|
router: object;
|
|
6
7
|
workflows?: Record<string, AnyWorkflowDefinition>;
|
|
8
|
+
telephony?: {
|
|
9
|
+
onMessageReceived?: (event: TelephonyMessageReceivedEvent) => void | Promise<void>;
|
|
10
|
+
};
|
|
7
11
|
}, options: {
|
|
8
12
|
code: string;
|
|
9
13
|
}): {
|
|
@@ -76,4 +80,13 @@ type ErrorOutcome = {
|
|
|
76
80
|
declare const workflowOutcomeSchema: z.ZodType<StepOutcome | SleepOutcome | StepErrorOutcome | DoneOutcome | ErrorOutcome>;
|
|
77
81
|
type WorkflowOutcome = z.infer<typeof workflowOutcomeSchema>;
|
|
78
82
|
//#endregion
|
|
79
|
-
|
|
83
|
+
//#region src/server/workflow-engine.d.ts
|
|
84
|
+
type Engine = {
|
|
85
|
+
invoke<T>(name: string, fn: () => Promise<T>): Promise<T>;
|
|
86
|
+
sleepUntil(name: string, until: Date): Promise<void>;
|
|
87
|
+
sleep(name: string, milliseconds: number): Promise<void>;
|
|
88
|
+
};
|
|
89
|
+
declare class WorkflowFailedError extends Error {}
|
|
90
|
+
declare function runWorkflowJournal(engine: Engine, invoke: (journal: JournalEntry[]) => Promise<WorkflowOutcome>): Promise<unknown>;
|
|
91
|
+
//#endregion
|
|
92
|
+
export { type Engine, type JournalEntry, WorkflowFailedError, type WorkflowInvocationRequest, type WorkflowOutcome, createEmailPixelInputSchema, createWorkerEntry, journalEntrySchema, listEmailTrackersInputSchema, markEmailTrackerSentBodySchema, runWorkflowJournal, workflowInvocationRequestSchema, workflowOutcomeSchema };
|
package/dist/_internal/server.js
CHANGED
|
@@ -1,80 +1,16 @@
|
|
|
1
|
+
import { c as listEmailTrackersInputSchema, t as createEmailPixelInputSchema, u as markEmailTrackerSentBodySchema } from "../email-DBTVhNB_.js";
|
|
2
|
+
import { a as workflowOutcomeSchema, c as HITCH_SESSION_HEADER, i as workflowInvocationRequestSchema, l as HITCH_USER_HEADER, n as runWorkflowJournal, o as HITCH_APPLET_HEADER, r as journalEntrySchema, s as HITCH_APPLET_TOKEN_HEADER, t as WorkflowFailedError } from "../workflow-engine-MogejNfF.js";
|
|
1
3
|
import { o as runWithRequestContext, r as toMilliseconds, t as MAX_DURATION_MS } from "../workflow-CTo2kZF1.js";
|
|
2
4
|
import { z } from "zod";
|
|
3
5
|
import { RPCHandler } from "@orpc/server/fetch";
|
|
4
|
-
//#region src/
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const journalEntrySchema = z.discriminatedUnion("kind", [
|
|
12
|
-
z.object({
|
|
13
|
-
kind: z.literal("step"),
|
|
14
|
-
id: z.string(),
|
|
15
|
-
name: z.string(),
|
|
16
|
-
result: z.unknown()
|
|
17
|
-
}),
|
|
18
|
-
z.object({
|
|
19
|
-
kind: z.literal("sleep"),
|
|
20
|
-
id: z.string(),
|
|
21
|
-
name: z.string(),
|
|
22
|
-
until: z.number()
|
|
23
|
-
}),
|
|
24
|
-
z.object({
|
|
25
|
-
kind: z.literal("attempt"),
|
|
26
|
-
id: z.string(),
|
|
27
|
-
name: z.string(),
|
|
28
|
-
error: z.string()
|
|
29
|
-
}),
|
|
30
|
-
z.object({
|
|
31
|
-
kind: z.literal("failed"),
|
|
32
|
-
id: z.string(),
|
|
33
|
-
name: z.string(),
|
|
34
|
-
error: z.string()
|
|
35
|
-
})
|
|
36
|
-
]);
|
|
37
|
-
const workflowInvocationRequestSchema = z.object({
|
|
38
|
-
payload: z.unknown(),
|
|
39
|
-
journal: z.array(journalEntrySchema)
|
|
6
|
+
//#region src/server/telephony-protocol.ts
|
|
7
|
+
const telephonyMessageReceivedEventSchema = z.object({
|
|
8
|
+
eventId: z.string(),
|
|
9
|
+
type: z.literal("message.received"),
|
|
10
|
+
messageId: z.string(),
|
|
11
|
+
numberId: z.string(),
|
|
12
|
+
occurredAt: z.iso.datetime()
|
|
40
13
|
});
|
|
41
|
-
const workflowOutcomeSchema = z.discriminatedUnion("kind", [
|
|
42
|
-
z.object({
|
|
43
|
-
kind: z.literal("step"),
|
|
44
|
-
id: z.string(),
|
|
45
|
-
name: z.string(),
|
|
46
|
-
result: z.unknown()
|
|
47
|
-
}),
|
|
48
|
-
z.object({
|
|
49
|
-
kind: z.literal("sleep"),
|
|
50
|
-
id: z.string(),
|
|
51
|
-
name: z.string(),
|
|
52
|
-
until: z.number()
|
|
53
|
-
}),
|
|
54
|
-
z.object({
|
|
55
|
-
kind: z.literal("step_error"),
|
|
56
|
-
id: z.string(),
|
|
57
|
-
name: z.string(),
|
|
58
|
-
message: z.string(),
|
|
59
|
-
retries: z.object({
|
|
60
|
-
limit: z.number(),
|
|
61
|
-
delayMs: z.number(),
|
|
62
|
-
backoff: z.enum([
|
|
63
|
-
"constant",
|
|
64
|
-
"linear",
|
|
65
|
-
"exponential"
|
|
66
|
-
])
|
|
67
|
-
})
|
|
68
|
-
}),
|
|
69
|
-
z.object({
|
|
70
|
-
kind: z.literal("done"),
|
|
71
|
-
result: z.unknown()
|
|
72
|
-
}),
|
|
73
|
-
z.object({
|
|
74
|
-
kind: z.literal("error"),
|
|
75
|
-
message: z.string()
|
|
76
|
-
})
|
|
77
|
-
]);
|
|
78
14
|
//#endregion
|
|
79
15
|
//#region src/server/workflow-run.ts
|
|
80
16
|
const MAX_RESULT_SIZE = 1048576;
|
|
@@ -209,6 +145,19 @@ function createWorkerEntry(server, options) {
|
|
|
209
145
|
}
|
|
210
146
|
}, async () => {
|
|
211
147
|
const pathname = new URL(request.url).pathname;
|
|
148
|
+
if (pathname === "/api/__hitch/telephony/message-received" && request.method === "POST") {
|
|
149
|
+
if (identity.kind !== "applet") return jsonError(403, "applet_identity_required");
|
|
150
|
+
const parsed = await request.json().then((body) => telephonyMessageReceivedEventSchema.safeParse(body)).catch(() => void 0);
|
|
151
|
+
if (!parsed?.success) return jsonError(400, "malformed_request");
|
|
152
|
+
const onMessageReceived = server.telephony?.onMessageReceived;
|
|
153
|
+
if (!onMessageReceived) return jsonError(404, "no_telephony_handler");
|
|
154
|
+
try {
|
|
155
|
+
await onMessageReceived(parsed.data);
|
|
156
|
+
return Response.json({ ok: true });
|
|
157
|
+
} catch (error) {
|
|
158
|
+
return jsonError(500, error instanceof Error ? error.message : String(error));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
212
161
|
if (pathname.startsWith(WORKFLOW_PATH_PREFIX)) {
|
|
213
162
|
const parsed = await request.json().then((body) => workflowInvocationRequestSchema.safeParse(body)).catch(() => void 0);
|
|
214
163
|
if (parsed === void 0 || !parsed.success) return jsonError(400, "malformed_request");
|
|
@@ -229,4 +178,4 @@ function createWorkerEntry(server, options) {
|
|
|
229
178
|
} };
|
|
230
179
|
}
|
|
231
180
|
//#endregion
|
|
232
|
-
export { createWorkerEntry, journalEntrySchema, workflowInvocationRequestSchema, workflowOutcomeSchema };
|
|
181
|
+
export { WorkflowFailedError, createEmailPixelInputSchema, createWorkerEntry, journalEntrySchema, listEmailTrackersInputSchema, markEmailTrackerSentBodySchema, runWorkflowJournal, workflowInvocationRequestSchema, workflowOutcomeSchema };
|
package/dist/applet.js
CHANGED
|
@@ -2,17 +2,22 @@
|
|
|
2
2
|
import { parseArgs } from "node:util";
|
|
3
3
|
//#region bin/applet.ts
|
|
4
4
|
const usage = `Usage:
|
|
5
|
+
applet login [--no-browser]
|
|
5
6
|
applet init <org> [--no-commit | --no-git] [--no-install]
|
|
6
7
|
applet bootstrap [--no-install]
|
|
7
8
|
applet new <code> [--client-only | --server-only] [--no-install]
|
|
8
|
-
applet generate
|
|
9
|
-
applet build
|
|
10
|
-
applet deploy
|
|
9
|
+
applet generate
|
|
10
|
+
applet build
|
|
11
|
+
applet deploy
|
|
11
12
|
applet logs [code] [--since <duration>] [--limit <count>]
|
|
12
13
|
[--search <text>] [--level <level>] [--filter <json>] [--run <id>] [--live]
|
|
13
14
|
applet variables set <KEY> [value] [--secret] [-y | --yes]
|
|
14
15
|
applet variables list
|
|
15
16
|
applet variables delete <KEY> [-y | --yes]
|
|
17
|
+
applet schedules run <code> [-y | --yes]
|
|
18
|
+
|
|
19
|
+
Login options:
|
|
20
|
+
--no-browser Print the sign-in URL without opening a browser.
|
|
16
21
|
|
|
17
22
|
Init options:
|
|
18
23
|
--no-commit Do not create the initial commit
|
|
@@ -27,15 +32,14 @@ New options:
|
|
|
27
32
|
Bootstrap options:
|
|
28
33
|
--no-install Do not run pnpm install
|
|
29
34
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
Schema changesets:
|
|
36
|
+
Set requiresChangeset in applet.manifest.ts to generate, build, and deploy
|
|
37
|
+
against a pending changeset. The release goes live when the changeset
|
|
38
|
+
applies in Hitch. After it applies, the field uses the live schema.
|
|
33
39
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
when the changeset applies. Manifest mismatches warn.
|
|
38
|
-
Run applet deploy in the workspace root to deploy every applet.
|
|
40
|
+
Run applet deploy in the workspace root to deploy every applet. In GitHub
|
|
41
|
+
Actions it skips an applet when neither its folder nor any file outside
|
|
42
|
+
applets/ changed since its current CI release.
|
|
39
43
|
|
|
40
44
|
Logs options:
|
|
41
45
|
--since <duration> Show logs from this period. Default: 1h. Maximum: 7d.
|
|
@@ -52,6 +56,11 @@ Variables options:
|
|
|
52
56
|
--secret Store the value as a secret.
|
|
53
57
|
-y, --yes Skip the live applet confirmation.
|
|
54
58
|
|
|
59
|
+
Schedules options:
|
|
60
|
+
-y, --yes Skip the schedule confirmation.
|
|
61
|
+
|
|
62
|
+
Login: applet login selects the organization from hitch.config.ts and shares
|
|
63
|
+
credentials with hitch login.
|
|
55
64
|
CI: set HITCH_API_KEY (and HITCH_API_URL for a non-default instance).
|
|
56
65
|
GitHub Actions: generate, build, and deploy use the job's OIDC token instead of
|
|
57
66
|
a key. Grant the job id-token: write.`;
|
|
@@ -59,13 +68,13 @@ async function main() {
|
|
|
59
68
|
const { positionals, values } = parseArgs({
|
|
60
69
|
allowPositionals: true,
|
|
61
70
|
options: {
|
|
62
|
-
changeset: { type: "string" },
|
|
63
71
|
"client-only": { type: "boolean" },
|
|
64
72
|
filter: { type: "string" },
|
|
65
73
|
run: { type: "string" },
|
|
66
74
|
level: { type: "string" },
|
|
67
75
|
limit: { type: "string" },
|
|
68
76
|
live: { type: "boolean" },
|
|
77
|
+
"no-browser": { type: "boolean" },
|
|
69
78
|
"no-commit": { type: "boolean" },
|
|
70
79
|
"no-git": { type: "boolean" },
|
|
71
80
|
"no-install": { type: "boolean" },
|
|
@@ -80,36 +89,41 @@ async function main() {
|
|
|
80
89
|
},
|
|
81
90
|
strict: true
|
|
82
91
|
});
|
|
92
|
+
if (positionals.length === 1 && positionals[0] === "login" && Object.keys(values).every((key) => key === "no-browser")) {
|
|
93
|
+
await (await import("./login-C7RKmmTe.js")).runLogin({ noBrowser: values["no-browser"] });
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (values["no-browser"] !== void 0) throw new Error(usage);
|
|
83
97
|
const noGitFlags = values["no-commit"] === void 0 && values["no-git"] === void 0;
|
|
84
98
|
const noVariableFlags = values.secret === void 0 && values.yes === void 0;
|
|
85
99
|
const noLogFlags = values.run === void 0 && values.filter === void 0 && values.level === void 0 && values.limit === void 0 && values.live === void 0 && values.search === void 0 && values.since === void 0;
|
|
86
100
|
const noModeFlags = values["client-only"] === void 0 && values["server-only"] === void 0;
|
|
87
|
-
if (positionals.length === 1 && positionals[0] === "bootstrap" &&
|
|
88
|
-
await (await import("./init-
|
|
101
|
+
if (positionals.length === 1 && positionals[0] === "bootstrap" && noModeFlags && noGitFlags && noVariableFlags && noLogFlags) {
|
|
102
|
+
await (await import("./init-jDEgYIXg.js")).runBootstrap({ install: values["no-install"] !== true });
|
|
89
103
|
return;
|
|
90
104
|
}
|
|
91
105
|
if (positionals.length === 1 && positionals[0] === "generate" && values["no-install"] === void 0 && noModeFlags && noGitFlags && noVariableFlags && noLogFlags) {
|
|
92
|
-
await (await import("./generate-
|
|
106
|
+
await (await import("./generate-CQ07Hdpo.js")).runGenerate({});
|
|
93
107
|
return;
|
|
94
108
|
}
|
|
95
109
|
if (positionals.length === 1 && positionals[0] === "build" && values["no-install"] === void 0 && noModeFlags && noGitFlags && noVariableFlags && noLogFlags) {
|
|
96
|
-
await (await import("./build-
|
|
110
|
+
await (await import("./build-CFCKKMn7.js")).runBuild();
|
|
97
111
|
return;
|
|
98
112
|
}
|
|
99
113
|
if (positionals.length === 1 && positionals[0] === "deploy" && values["no-install"] === void 0 && noModeFlags && noGitFlags && noVariableFlags && noLogFlags) {
|
|
100
|
-
await (await import("./deploy-
|
|
114
|
+
await (await import("./deploy-MQWLG2EA.js")).runDeploy({});
|
|
101
115
|
return;
|
|
102
116
|
}
|
|
103
|
-
if (positionals.length === 2 && positionals[0] === "init" &&
|
|
104
|
-
await (await import("./init-
|
|
117
|
+
if (positionals.length === 2 && positionals[0] === "init" && noModeFlags && noVariableFlags && noLogFlags) {
|
|
118
|
+
await (await import("./init-jDEgYIXg.js")).runInit(positionals[1], {
|
|
105
119
|
commit: values["no-commit"] !== true,
|
|
106
120
|
git: values["no-git"] !== true,
|
|
107
121
|
install: values["no-install"] !== true
|
|
108
122
|
});
|
|
109
123
|
return;
|
|
110
124
|
}
|
|
111
|
-
if (positionals.length === 2 && positionals[0] === "new" &&
|
|
112
|
-
await (await import("./init-
|
|
125
|
+
if (positionals.length === 2 && positionals[0] === "new" && noGitFlags && noVariableFlags && noLogFlags) {
|
|
126
|
+
await (await import("./init-jDEgYIXg.js")).runNew({
|
|
113
127
|
code: positionals[1],
|
|
114
128
|
clientOnly: values["client-only"],
|
|
115
129
|
install: values["no-install"] !== true,
|
|
@@ -117,8 +131,8 @@ async function main() {
|
|
|
117
131
|
});
|
|
118
132
|
return;
|
|
119
133
|
}
|
|
120
|
-
if ((positionals.length === 1 || positionals.length === 2) && positionals[0] === "logs" && values
|
|
121
|
-
await (await import("./logs-
|
|
134
|
+
if ((positionals.length === 1 || positionals.length === 2) && positionals[0] === "logs" && values["no-install"] === void 0 && noModeFlags && noGitFlags && noVariableFlags) {
|
|
135
|
+
await (await import("./logs-DgjgAxj2.js")).runLogs({
|
|
122
136
|
code: positionals[1],
|
|
123
137
|
filter: values.filter,
|
|
124
138
|
run: values.run,
|
|
@@ -130,8 +144,15 @@ async function main() {
|
|
|
130
144
|
});
|
|
131
145
|
return;
|
|
132
146
|
}
|
|
133
|
-
if (
|
|
134
|
-
await (await import("./
|
|
147
|
+
if (positionals.length === 3 && positionals[0] === "schedules" && positionals[1] === "run" && values["no-install"] === void 0 && noModeFlags && noGitFlags && noLogFlags && values.secret === void 0) {
|
|
148
|
+
await (await import("./schedules-dhU1yyFr.js")).runSchedule({
|
|
149
|
+
code: positionals[2],
|
|
150
|
+
yes: values.yes
|
|
151
|
+
});
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if ((positionals.length === 3 || positionals.length === 4) && positionals[0] === "variables" && positionals[1] === "set" && values["no-install"] === void 0 && noModeFlags && noGitFlags && noLogFlags) {
|
|
155
|
+
await (await import("./variables-Bwnr7oxf.js")).runVariablesSet({
|
|
135
156
|
key: positionals[2],
|
|
136
157
|
secret: values.secret,
|
|
137
158
|
value: positionals[3],
|
|
@@ -139,12 +160,12 @@ async function main() {
|
|
|
139
160
|
});
|
|
140
161
|
return;
|
|
141
162
|
}
|
|
142
|
-
if (positionals.length === 2 && positionals[0] === "variables" &&
|
|
143
|
-
await (await import("./variables-
|
|
163
|
+
if (positionals.length === 2 && positionals[0] === "variables" && positionals[1] === "list" && values["no-install"] === void 0 && noModeFlags && noGitFlags && noVariableFlags && noLogFlags) {
|
|
164
|
+
await (await import("./variables-Bwnr7oxf.js")).runVariablesList({});
|
|
144
165
|
return;
|
|
145
166
|
}
|
|
146
|
-
if (positionals.length === 3 && positionals[0] === "variables" &&
|
|
147
|
-
await (await import("./variables-
|
|
167
|
+
if (positionals.length === 3 && positionals[0] === "variables" && positionals[1] === "delete" && values["no-install"] === void 0 && noModeFlags && noGitFlags && values.secret === void 0 && noLogFlags) {
|
|
168
|
+
await (await import("./variables-Bwnr7oxf.js")).runVariablesDelete({
|
|
148
169
|
key: positionals[2],
|
|
149
170
|
yes: values.yes
|
|
150
171
|
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { r as prepareAppletProject } from "./project-
|
|
1
|
+
import { i as prepareAppletProjects, r as prepareAppletProject } from "./project-CbYPtpPq.js";
|
|
2
2
|
import { rm } from "node:fs/promises";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
|
+
import { createInterface } from "node:readline";
|
|
5
6
|
//#region src/build.ts
|
|
6
7
|
function isMissingFile(error) {
|
|
7
8
|
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
@@ -29,13 +30,19 @@ async function runPnpm(args, cwd) {
|
|
|
29
30
|
});
|
|
30
31
|
});
|
|
31
32
|
}
|
|
32
|
-
async function buildApplet(appletRoot,
|
|
33
|
-
|
|
33
|
+
async function buildApplet(appletRoot, outDir = ".hitch/dist") {
|
|
34
|
+
await runPnpm(["install"], await prepareAppletProject(appletRoot));
|
|
35
|
+
await buildPreparedApplet(appletRoot, outDir);
|
|
36
|
+
}
|
|
37
|
+
async function prepareWorkspaceBuild(workspaceRoot, appletRoots) {
|
|
38
|
+
await prepareAppletProjects(workspaceRoot, appletRoots);
|
|
39
|
+
await runPnpm(["install"], workspaceRoot);
|
|
40
|
+
}
|
|
41
|
+
async function buildPreparedApplet(appletRoot, outDir = ".hitch/dist", logPrefix = "") {
|
|
34
42
|
await rm(resolve(appletRoot, ".hitch"), {
|
|
35
43
|
force: true,
|
|
36
44
|
recursive: true
|
|
37
45
|
});
|
|
38
|
-
await runPnpm(["install"], workspaceRoot);
|
|
39
46
|
await new Promise((resolveBuild, rejectBuild) => {
|
|
40
47
|
let finished = false;
|
|
41
48
|
const child = spawn("pnpm", [
|
|
@@ -46,13 +53,21 @@ async function buildApplet(appletRoot, changesetId, outDir = ".hitch/dist") {
|
|
|
46
53
|
outDir
|
|
47
54
|
], {
|
|
48
55
|
cwd: appletRoot,
|
|
49
|
-
stdio: "inherit"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
stdio: logPrefix === "" ? "inherit" : [
|
|
57
|
+
"ignore",
|
|
58
|
+
"pipe",
|
|
59
|
+
"pipe"
|
|
60
|
+
],
|
|
61
|
+
shell: process.platform === "win32"
|
|
55
62
|
});
|
|
63
|
+
if (child.stdout) createInterface({
|
|
64
|
+
input: child.stdout,
|
|
65
|
+
crlfDelay: Infinity
|
|
66
|
+
}).on("line", (line) => console.log(`${logPrefix}${line}`));
|
|
67
|
+
if (child.stderr) createInterface({
|
|
68
|
+
input: child.stderr,
|
|
69
|
+
crlfDelay: Infinity
|
|
70
|
+
}).on("line", (line) => console.error(`${logPrefix}${line}`));
|
|
56
71
|
child.once("error", (error) => {
|
|
57
72
|
finished = true;
|
|
58
73
|
rejectBuild(isMissingFile(error) ? /* @__PURE__ */ new Error("pnpm was not found. Install pnpm, then run applet build again.") : error);
|
|
@@ -68,8 +83,8 @@ async function buildApplet(appletRoot, changesetId, outDir = ".hitch/dist") {
|
|
|
68
83
|
});
|
|
69
84
|
});
|
|
70
85
|
}
|
|
71
|
-
async function runBuild(
|
|
72
|
-
await buildApplet(process.cwd()
|
|
86
|
+
async function runBuild() {
|
|
87
|
+
await buildApplet(process.cwd());
|
|
73
88
|
}
|
|
74
89
|
//#endregion
|
|
75
|
-
export { runBuild as n, buildApplet as t };
|
|
90
|
+
export { runBuild as i, buildPreparedApplet as n, prepareWorkspaceBuild as r, buildApplet as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as resolveInstanceUrl } from "./workspace-XVtM12ZC.js";
|
|
2
2
|
//#region src/ci.ts
|
|
3
3
|
function githubActionsContext(env = process.env) {
|
|
4
4
|
if (env.GITHUB_ACTIONS !== "true" || env.HITCH_API_KEY) return void 0;
|
|
@@ -34,9 +34,10 @@ async function fetchWithGitHubActions(context, options, request) {
|
|
|
34
34
|
const instanceUrl = await resolveInstanceUrl(void 0, options.store);
|
|
35
35
|
return request(instanceUrl, await githubActionsToken(context, instanceUrl, options.fetch ?? globalThis.fetch));
|
|
36
36
|
}
|
|
37
|
-
async function loadGitHubActionsBuildInputs(context, options) {
|
|
37
|
+
async function loadGitHubActionsBuildInputs(context, options, requiresChangeset) {
|
|
38
38
|
const fetcher = options.fetch ?? globalThis.fetch;
|
|
39
|
-
const
|
|
39
|
+
const query = requiresChangeset === void 0 ? "" : `?changeset_id=${encodeURIComponent(requiresChangeset)}`;
|
|
40
|
+
const response = await fetchWithGitHubActions(context, options, (instanceUrl, accessToken) => fetcher(`${instanceUrl}/api/ci/v1/build-inputs${query}`, {
|
|
40
41
|
method: "POST",
|
|
41
42
|
headers: { Authorization: `Bearer ${accessToken}` }
|
|
42
43
|
}));
|
|
@@ -48,5 +49,20 @@ async function loadGitHubActionsBuildInputs(context, options) {
|
|
|
48
49
|
theme: body.theme
|
|
49
50
|
};
|
|
50
51
|
}
|
|
52
|
+
async function loadGitHubActionsSources(context, options) {
|
|
53
|
+
const fetcher = options.fetch ?? globalThis.fetch;
|
|
54
|
+
const response = await fetchWithGitHubActions(context, options, (instanceUrl, accessToken) => fetcher(`${instanceUrl}/api/ci/v1/applets`, { headers: { Authorization: `Bearer ${accessToken}` } }));
|
|
55
|
+
if (!response.ok) throw await hitchResponseError(response, "Failed to load the CI applets");
|
|
56
|
+
const body = await response.json();
|
|
57
|
+
if (body === null || typeof body !== "object" || !("data" in body) || !Array.isArray(body.data)) throw new Error("The CI applets response must contain a data array.");
|
|
58
|
+
return body.data.map((source) => {
|
|
59
|
+
if (source === null || typeof source !== "object" || !("code" in source) || typeof source.code !== "string" || !("source_directory" in source) || typeof source.source_directory !== "string" || !("git_sha" in source) || source.git_sha !== null && typeof source.git_sha !== "string") throw new Error("Each CI applet must contain code, source_directory, and git_sha fields.");
|
|
60
|
+
return {
|
|
61
|
+
code: source.code,
|
|
62
|
+
sourceDirectory: source.source_directory,
|
|
63
|
+
gitSha: source.git_sha
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
}
|
|
51
67
|
//#endregion
|
|
52
|
-
export { loadGitHubActionsBuildInputs as i, githubActionsContext as n, hitchResponseError as r, fetchWithGitHubActions as t };
|
|
68
|
+
export { loadGitHubActionsSources as a, loadGitHubActionsBuildInputs as i, githubActionsContext as n, hitchResponseError as r, fetchWithGitHubActions as t };
|
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { f as RegisteredContract, m as RegisteredRouter, p as RegisteredManifest } from "./manifest-
|
|
2
|
-
import { t as NarrowContract } from "./contract-
|
|
1
|
+
import { f as RegisteredContract, m as RegisteredRouter, p as RegisteredManifest } from "./manifest-DD_SGkS6.js";
|
|
2
|
+
import { t as NarrowContract } from "./contract-rP_eV9PK.js";
|
|
3
3
|
import { t as WithBatchRecorder } from "./batch-Qa4B4-Yp.js";
|
|
4
4
|
import { o as WithFilesUpload } from "./upload-YbRKEqR6.js";
|
|
5
5
|
import { RouterClient } from "@orpc/server";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as AppletManifest } from "./manifest-
|
|
1
|
+
import { n as AppletManifest } from "./manifest-DD_SGkS6.js";
|
|
2
2
|
import { AnyContractRouter } from "@orpc/contract";
|
|
3
3
|
//#region src/hitch/contract.d.ts
|
|
4
4
|
type ManifestObjectFor<Manifest, Code> = Manifest extends {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as githubActionsContext, r as hitchResponseError, t as fetchWithGitHubActions } from "./ci-
|
|
3
|
-
import { r as
|
|
4
|
-
import { t as buildApplet } from "./build-DIsKQQT0.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-DANx9Hgg.js";
|
|
5
4
|
import { createRequire } from "node:module";
|
|
6
5
|
import { existsSync } from "node:fs";
|
|
7
6
|
import { readFile, readdir } from "node:fs/promises";
|
|
@@ -2314,6 +2313,12 @@ var import_tar_stream = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin((
|
|
|
2314
2313
|
exports.pack = require_pack();
|
|
2315
2314
|
})))(), 1);
|
|
2316
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
|
+
};
|
|
2317
2322
|
function isErrno(error, code) {
|
|
2318
2323
|
return error instanceof Error && "code" in error && error.code === code;
|
|
2319
2324
|
}
|
|
@@ -2401,7 +2406,6 @@ async function deployWithOrgAuth(appletRoot, distDir, manifest, options) {
|
|
|
2401
2406
|
const body = releaseForm(manifest, bundle);
|
|
2402
2407
|
body.set("gitDirty", String(git.gitDirty));
|
|
2403
2408
|
if (git.gitSha !== void 0) body.set("gitSha", git.gitSha);
|
|
2404
|
-
if (options.changesetId !== void 0) body.set("changeset_id", options.changesetId);
|
|
2405
2409
|
return fetcher(`${instanceUrl}/api/v1/applets/${manifest.code}/releases`, {
|
|
2406
2410
|
method: "POST",
|
|
2407
2411
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
@@ -2422,21 +2426,21 @@ async function deployWithGitHubActions(context, appletRoot, distDir, manifest, o
|
|
|
2422
2426
|
});
|
|
2423
2427
|
});
|
|
2424
2428
|
}
|
|
2425
|
-
async function deployApplet(appletRoot, options) {
|
|
2429
|
+
async function deployApplet(appletRoot, options, logPrefix = "") {
|
|
2426
2430
|
const github = githubActionsContext(options.store?.env ?? process.env);
|
|
2427
|
-
if (
|
|
2428
|
-
if (options.dist === void 0) await buildApplet(appletRoot, options.changesetId);
|
|
2431
|
+
if (options.dist === void 0) await buildApplet(appletRoot);
|
|
2429
2432
|
const distDir = resolve(appletRoot, options.dist ?? ".hitch/dist");
|
|
2430
2433
|
const manifest = await readManifest(distDir);
|
|
2431
2434
|
const response = github === void 0 ? await deployWithOrgAuth(appletRoot, distDir, manifest, options) : await deployWithGitHubActions(github, appletRoot, distDir, manifest, options);
|
|
2432
2435
|
if (!response.ok) throw await hitchResponseError(response, "Deploy failed");
|
|
2433
2436
|
const result = await response.json();
|
|
2434
|
-
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.`);
|
|
2435
2439
|
if (result.warnings?.length) {
|
|
2436
|
-
for (const warning of result.warnings) console.warn(
|
|
2437
|
-
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.`);
|
|
2438
2442
|
}
|
|
2439
|
-
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.`);
|
|
2440
2444
|
return result;
|
|
2441
2445
|
}
|
|
2442
2446
|
async function workspaceAppletRoots(workspaceRoot) {
|
|
@@ -2448,17 +2452,128 @@ async function workspaceAppletRoots(workspaceRoot) {
|
|
|
2448
2452
|
if (roots.length === 0) throw new Error(`No applets were found under ${appletsDir}.`);
|
|
2449
2453
|
return roots;
|
|
2450
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
|
+
}
|
|
2451
2534
|
async function runDeploy(options) {
|
|
2452
2535
|
const root = process.cwd();
|
|
2536
|
+
const github = githubActionsContext(options.store?.env ?? process.env);
|
|
2453
2537
|
if (existsSync(resolve(root, "applet.manifest.ts"))) {
|
|
2454
2538
|
await deployApplet(root, options);
|
|
2455
2539
|
return;
|
|
2456
2540
|
}
|
|
2457
2541
|
if (readWorkspaceConfig(root) === void 0) throw new Error("Run applet deploy in an applet folder or in the workspace root.");
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
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;
|
|
2461
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);
|
|
2462
2577
|
}
|
|
2463
2578
|
//#endregion
|
|
2464
2579
|
export { runDeploy };
|