atmn 2.0.41 → 2.0.43
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/bin.js +405 -392
- package/dist/index.js +20 -21
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/actions/pull/webhooks/appendWebhook.ts +6 -4
- package/src/actions/pull/webhooks/applyWebhooksPull.ts +28 -105
- package/src/actions/pull/webhooks/deleteWebhook.ts +59 -0
- package/src/actions/pull/webhooks/locateWebhook.ts +9 -1
- package/src/actions/pull/webhooks/readWebhookEnvs.ts +7 -12
- package/src/actions/pull/webhooks/types.ts +3 -2
- package/src/actions/pull/webhooks/updateWebhook.ts +37 -42
- package/src/actions/pull/webhooks/webhookEnvSkipWarning.ts +29 -0
- package/src/actions/pull/webhooks/webhookPullEnvs.ts +5 -5
- package/src/actions/pull.ts +1 -1
- package/src/actions/push.ts +30 -12
- package/src/actions/webhooks/applyWebhooks.ts +41 -10
- package/src/actions/webhooks/previewWebhookEnvs.ts +213 -0
- package/src/actions/webhooks/resolveWebhooksForEnv.ts +5 -16
- package/src/actions/webhooks/types/webhookEnv.ts +1 -3
- package/src/actions/webhooks/webhookEnvFailures.ts +27 -0
- package/src/actions/webhooks/webhookSecretName.ts +10 -9
- package/src/actions/webhooks/writeWebhookSecrets.ts +5 -2
- package/src/auth/runOAuthFlow.ts +1 -1
- package/src/cli.ts +23 -25
- package/src/generated/apiRoutes.ts +43 -12
- package/src/generated/client.ts +10 -10
- package/src/generated/emit.ts +4 -4
- package/src/generated/emitRuntime.ts +3 -3
- package/src/generated/lintRules.ts +16 -20
- package/src/generated/lintRuntime.ts +10 -0
- package/src/generated/skills.ts +10 -10
- package/src/generated/webhooks.ts +7 -9
- package/src/generated/wire.ts +0 -1
- package/src/render/renderPreview.ts +25 -9
- package/src/render/renderWebhooks.ts +9 -3
- package/src/actions/pull/webhooks/removeWebhookEnv.ts +0 -141
- package/src/actions/webhooks/isDashboardWebhook.ts +0 -9
- package/src/actions/webhooks/newWebhookId.ts +0 -4
- package/src/actions/webhooks/previewWebhooks.ts +0 -40
- package/src/actions/webhooks/resolveWebhookEnv.ts +0 -34
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { SYNCED_LISTS } from "../../../generated/emit";
|
|
2
|
-
import { deleteFixtureLiteral } from "../../../surgery/deleteFixtureLiteral";
|
|
3
|
-
import { deleteReference } from "../../../surgery/deleteReference";
|
|
4
|
-
import { fixtureObjectOf } from "../../../surgery/findFixture";
|
|
5
|
-
import { patchFixturePaths } from "../../../surgery/patchFixturePaths";
|
|
6
|
-
import {
|
|
7
|
-
fixtureKeyOf,
|
|
8
|
-
readFixtureValue,
|
|
9
|
-
} from "../../../surgery/readFixtureValue";
|
|
10
|
-
import { resolveCollectionTarget } from "../resolveCollectionTarget";
|
|
11
|
-
import { locateWebhook } from "./locateWebhook";
|
|
12
|
-
import type { PullFiles, StatedWebhook, WebhookEditResult } from "./types";
|
|
13
|
-
import { nonLiteralUrlWarning } from "./updateWebhook";
|
|
14
|
-
|
|
15
|
-
const SPEC = SYNCED_LISTS.webhooks;
|
|
16
|
-
|
|
17
|
-
/** Read from source, not the evaluated config: a key whose code evaluates to
|
|
18
|
-
* undefined is still a key the user wrote. */
|
|
19
|
-
const urlMapIsEmpty = ({
|
|
20
|
-
pull,
|
|
21
|
-
id,
|
|
22
|
-
}: {
|
|
23
|
-
pull: PullFiles;
|
|
24
|
-
id: string;
|
|
25
|
-
}): boolean => {
|
|
26
|
-
const located = locateWebhook({ pull, id });
|
|
27
|
-
const object = located === null ? null : fixtureObjectOf(located.node);
|
|
28
|
-
const member = object
|
|
29
|
-
?.namedChildren()
|
|
30
|
-
.find(
|
|
31
|
-
(child) =>
|
|
32
|
-
child.kind() === "pair" &&
|
|
33
|
-
child.field("key") !== null &&
|
|
34
|
-
fixtureKeyOf({ node: child.field("key") as never }) === "url",
|
|
35
|
-
);
|
|
36
|
-
const value = member?.field("value");
|
|
37
|
-
if (value === null || value === undefined) return false;
|
|
38
|
-
const read = readFixtureValue({ node: value });
|
|
39
|
-
return (
|
|
40
|
-
typeof read === "object" && read !== null && Object.keys(read).length === 0
|
|
41
|
-
);
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
/** The whole `webhook({...})`, and any export reference left pointing at it. */
|
|
45
|
-
const deleteWebhook = ({
|
|
46
|
-
pull,
|
|
47
|
-
id,
|
|
48
|
-
}: {
|
|
49
|
-
pull: PullFiles;
|
|
50
|
-
id: string;
|
|
51
|
-
}): boolean => {
|
|
52
|
-
const located = locateWebhook({ pull, id });
|
|
53
|
-
if (located === null) return false;
|
|
54
|
-
const removed = deleteFixtureLiteral({
|
|
55
|
-
source: located.source,
|
|
56
|
-
builder: SPEC.builder,
|
|
57
|
-
idField: SPEC.idField,
|
|
58
|
-
id,
|
|
59
|
-
});
|
|
60
|
-
if (removed === null) return false;
|
|
61
|
-
pull.files.set(located.file, removed.source);
|
|
62
|
-
if (removed.exportedName !== undefined) {
|
|
63
|
-
const referenceFiles = new Set([pull.configPath]);
|
|
64
|
-
const target = resolveCollectionTarget({
|
|
65
|
-
configPath: pull.configPath,
|
|
66
|
-
files: pull.files,
|
|
67
|
-
collection: "webhooks",
|
|
68
|
-
});
|
|
69
|
-
if (target !== null) referenceFiles.add(target.file);
|
|
70
|
-
for (const file of referenceFiles)
|
|
71
|
-
pull.files.set(
|
|
72
|
-
file,
|
|
73
|
-
deleteReference({
|
|
74
|
-
source: pull.files.get(file) ?? "",
|
|
75
|
-
name: removed.exportedName,
|
|
76
|
-
}),
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
return true;
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Rule 4: the server has no such webhook in this env, so the env's key goes;
|
|
84
|
-
* a map left empty registers the webhook nowhere, so the webhook goes too.
|
|
85
|
-
*/
|
|
86
|
-
export const removeWebhookEnv = ({
|
|
87
|
-
pull,
|
|
88
|
-
stated,
|
|
89
|
-
envKey,
|
|
90
|
-
}: {
|
|
91
|
-
pull: PullFiles;
|
|
92
|
-
stated: StatedWebhook;
|
|
93
|
-
envKey: string;
|
|
94
|
-
}): WebhookEditResult => {
|
|
95
|
-
const result: WebhookEditResult = { lines: [], warnings: [], unlocated: [] };
|
|
96
|
-
const located = locateWebhook({ pull, id: stated.id });
|
|
97
|
-
const patched =
|
|
98
|
-
located === null
|
|
99
|
-
? null
|
|
100
|
-
: patchFixturePaths({
|
|
101
|
-
source: located.source,
|
|
102
|
-
builder: SPEC.builder,
|
|
103
|
-
idField: SPEC.idField,
|
|
104
|
-
id: stated.id,
|
|
105
|
-
assignments: [{ path: ["url", envKey], text: null }],
|
|
106
|
-
});
|
|
107
|
-
if (located === null || patched === null) {
|
|
108
|
-
result.unlocated.push({
|
|
109
|
-
id: stated.id,
|
|
110
|
-
action: `remove url.${envKey} by hand`,
|
|
111
|
-
});
|
|
112
|
-
return result;
|
|
113
|
-
}
|
|
114
|
-
if (patched.skipped.length > 0) {
|
|
115
|
-
result.warnings.push(
|
|
116
|
-
nonLiteralUrlWarning({
|
|
117
|
-
id: stated.id,
|
|
118
|
-
envKey,
|
|
119
|
-
server: undefined,
|
|
120
|
-
config: stated.url?.[envKey],
|
|
121
|
-
}),
|
|
122
|
-
);
|
|
123
|
-
return result;
|
|
124
|
-
}
|
|
125
|
-
if (patched.source === located.source) return result;
|
|
126
|
-
pull.files.set(located.file, patched.source);
|
|
127
|
-
if (urlMapIsEmpty({ pull, id: stated.id })) {
|
|
128
|
-
if (deleteWebhook({ pull, id: stated.id })) {
|
|
129
|
-
result.lines.push(`- webhook ${stated.id}`);
|
|
130
|
-
return result;
|
|
131
|
-
}
|
|
132
|
-
// Only a literal can be deleted; saying so beats leaving `url: {}` behind.
|
|
133
|
-
result.unlocated.push({
|
|
134
|
-
id: stated.id,
|
|
135
|
-
action: "delete the webhook by hand: its url map is now empty",
|
|
136
|
-
});
|
|
137
|
-
return result;
|
|
138
|
-
}
|
|
139
|
-
result.lines.push(`- webhook ${stated.id} url.${envKey}`);
|
|
140
|
-
return result;
|
|
141
|
-
};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/** Svix's own endpoint id: `ep_` and a 27-character KSUID. */
|
|
2
|
-
const SVIX_ENDPOINT_ID = /^ep_[0-9A-Za-z]{27}$/;
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* A webhook made in the dashboard has no id of ours, so the API lists it under
|
|
6
|
-
* Svix's endpoint id. A config never manages one.
|
|
7
|
-
*/
|
|
8
|
-
export const isDashboardWebhook = ({ id }: { id: string }): boolean =>
|
|
9
|
-
SVIX_ENDPOINT_ID.test(id);
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type { AutumnClient, SyncWebhooksParams } from "../../generated/client";
|
|
2
|
-
import type { WebhooksPreview } from "../../render/renderWebhooks";
|
|
3
|
-
import { resolveWebhooksForEnv } from "./resolveWebhooksForEnv";
|
|
4
|
-
import type { WebhookEnv } from "./types/webhookEnv";
|
|
5
|
-
|
|
6
|
-
export type WebhooksLane = {
|
|
7
|
-
preview: WebhooksPreview;
|
|
8
|
-
/** Exactly what the preview was asked about, so the sync applies the same set. */
|
|
9
|
-
body: SyncWebhooksParams;
|
|
10
|
-
env: WebhookEnv;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
/** Absent when the config states no `webhooks`: an omitted list manages nothing. */
|
|
14
|
-
export const previewWebhooks = async ({
|
|
15
|
-
client,
|
|
16
|
-
rows,
|
|
17
|
-
webhookEnv,
|
|
18
|
-
}: {
|
|
19
|
-
client: AutumnClient;
|
|
20
|
-
rows: Record<string, unknown>[] | undefined;
|
|
21
|
-
webhookEnv: (() => Promise<WebhookEnv>) | undefined;
|
|
22
|
-
}): Promise<WebhooksLane | undefined> => {
|
|
23
|
-
if (rows === undefined) return undefined;
|
|
24
|
-
if (webhookEnv === undefined)
|
|
25
|
-
throw new Error("webhooks need the target environment to pick each url.");
|
|
26
|
-
const env = await webhookEnv();
|
|
27
|
-
const webhooks = resolveWebhooksForEnv({
|
|
28
|
-
rows,
|
|
29
|
-
envKey: env.key,
|
|
30
|
-
});
|
|
31
|
-
const body = { webhooks };
|
|
32
|
-
const { changes, errors } = await client.previewSyncWebhooks(body);
|
|
33
|
-
// Refused items (several dashboard webhooks on one URL, say) fail the lane
|
|
34
|
-
// here, beside every other lane's errors, rather than at apply.
|
|
35
|
-
if (errors.length > 0)
|
|
36
|
-
throw new Error(
|
|
37
|
-
errors.map(({ id, message }) => `${id}: ${message}`).join("\n"),
|
|
38
|
-
);
|
|
39
|
-
return { preview: { changes }, body, env };
|
|
40
|
-
};
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { Target } from "../../env/resolveTarget";
|
|
2
|
-
import { sandboxSlug } from "../../generated/sandboxName";
|
|
3
|
-
import type { FetchOrgInfo, OrgInfo } from "../env/types/orgInfo";
|
|
4
|
-
import type { WebhookEnv } from "./types/webhookEnv";
|
|
5
|
-
|
|
6
|
-
/** `/organization/me`, asked at most once per command. */
|
|
7
|
-
const memoized = (fetchOrgInfo: FetchOrgInfo): FetchOrgInfo => {
|
|
8
|
-
let pending: Promise<OrgInfo> | undefined;
|
|
9
|
-
return () => {
|
|
10
|
-
pending ??= fetchOrgInfo();
|
|
11
|
-
return pending;
|
|
12
|
-
};
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* `-p` is `live` and the default sandbox is `sandbox`, with no lookup. A named
|
|
17
|
-
* sandbox is keyed by its slug, which only its name gives: one lookup.
|
|
18
|
-
*/
|
|
19
|
-
export const resolveWebhookEnv = async ({
|
|
20
|
-
target,
|
|
21
|
-
prod,
|
|
22
|
-
fetchOrgInfo,
|
|
23
|
-
}: {
|
|
24
|
-
target: Target;
|
|
25
|
-
prod: boolean;
|
|
26
|
-
fetchOrgInfo: FetchOrgInfo;
|
|
27
|
-
}): Promise<WebhookEnv> => {
|
|
28
|
-
const orgInfo = memoized(fetchOrgInfo);
|
|
29
|
-
const orgId = async () => (await orgInfo()).id;
|
|
30
|
-
if (prod) return { key: "live", live: true, orgId };
|
|
31
|
-
if (target.sandboxId === undefined)
|
|
32
|
-
return { key: "sandbox", live: false, orgId };
|
|
33
|
-
return { key: sandboxSlug((await orgInfo()).name), live: false, orgId };
|
|
34
|
-
};
|