@flytedesk/app-kit 0.3.0 → 0.4.0

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 (56) hide show
  1. package/README.md +6 -3
  2. package/dist/bigquery/client.d.ts +18 -0
  3. package/dist/bigquery/client.js +37 -0
  4. package/dist/bigquery/client.js.map +1 -0
  5. package/dist/bigquery/errors.d.ts +26 -0
  6. package/dist/bigquery/errors.js +80 -0
  7. package/dist/bigquery/errors.js.map +1 -0
  8. package/dist/bigquery/extract.d.ts +15 -0
  9. package/dist/bigquery/extract.js +88 -0
  10. package/dist/bigquery/extract.js.map +1 -0
  11. package/dist/bigquery/index.d.ts +47 -0
  12. package/dist/bigquery/index.js +46 -0
  13. package/dist/bigquery/index.js.map +1 -0
  14. package/dist/bigquery/labels.d.ts +16 -0
  15. package/dist/bigquery/labels.js +26 -0
  16. package/dist/bigquery/labels.js.map +1 -0
  17. package/dist/bigquery/query.d.ts +16 -0
  18. package/dist/bigquery/query.js +86 -0
  19. package/dist/bigquery/query.js.map +1 -0
  20. package/dist/bigquery/types.d.ts +155 -0
  21. package/dist/bigquery/types.js +15 -0
  22. package/dist/bigquery/types.js.map +1 -0
  23. package/dist/chat/callback-secret.d.ts +21 -0
  24. package/dist/chat/callback-secret.js +33 -0
  25. package/dist/chat/callback-secret.js.map +1 -0
  26. package/dist/chat/env.d.ts +27 -0
  27. package/dist/chat/env.js +44 -0
  28. package/dist/chat/env.js.map +1 -0
  29. package/dist/chat/index.d.ts +72 -0
  30. package/dist/chat/index.js +70 -0
  31. package/dist/chat/index.js.map +1 -0
  32. package/dist/chat/launcher.d.ts +43 -0
  33. package/dist/chat/launcher.js +103 -0
  34. package/dist/chat/launcher.js.map +1 -0
  35. package/dist/chat/mcp-protocol.d.ts +96 -0
  36. package/dist/chat/mcp-protocol.js +200 -0
  37. package/dist/chat/mcp-protocol.js.map +1 -0
  38. package/dist/chat/mcp-server.d.ts +17 -0
  39. package/dist/chat/mcp-server.js +71 -0
  40. package/dist/chat/mcp-server.js.map +1 -0
  41. package/dist/chat/types.d.ts +91 -0
  42. package/dist/chat/types.js +5 -0
  43. package/dist/chat/types.js.map +1 -0
  44. package/dist/cli/flags-sync.js +2 -5
  45. package/dist/cli/flags-sync.js.map +1 -1
  46. package/dist/cli/profile-sync.js +2 -5
  47. package/dist/cli/profile-sync.js.map +1 -1
  48. package/dist/cli/sync-engine.d.ts +32 -0
  49. package/dist/cli/sync-engine.js +190 -23
  50. package/dist/cli/sync-engine.js.map +1 -1
  51. package/dist/cli/trace-sync.js +2 -5
  52. package/dist/cli/trace-sync.js.map +1 -1
  53. package/package.json +10 -1
  54. package/scripts/pending-release-count.mjs +37 -0
  55. package/scripts/release.sh +126 -0
  56. package/scripts/release.test.ts +205 -0
@@ -0,0 +1,205 @@
1
+ import { describe, expect, it, beforeAll } from "vitest";
2
+ import { spawnSync } from "node:child_process";
3
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join, resolve } from "node:path";
6
+
7
+ // scripts/release.sh refuses to do anything except from the sanctioned
8
+ // deploy clone, with a clean tree, without a live lock, and only for a
9
+ // commit reachable from origin/main. Those guards run before npm ci / the
10
+ // gates, so they're cheap and fully testable against throwaway git repos
11
+ // (no Docker, no network, no real npm publish).
12
+
13
+ const SCRIPT_PATH = resolve(__dirname, "release.sh");
14
+
15
+ // On Windows, a bare `bash` on PATH can resolve to the WSL launcher in
16
+ // System32 instead of Git Bash, which can't reliably run against a
17
+ // C:/Users/... working tree. Find Git Bash explicitly when it exists;
18
+ // fall back to `bash` (e.g. on Linux CI, where `bash` is unambiguous).
19
+ function resolveBash(): string {
20
+ const candidates = [
21
+ "C:\\Program Files\\Git\\bin\\bash.exe",
22
+ "C:\\Program Files\\Git\\usr\\bin\\bash.exe",
23
+ ];
24
+ for (const c of candidates) {
25
+ if (existsSync(c)) return c;
26
+ }
27
+ return "bash";
28
+ }
29
+
30
+ const BASH = resolveBash();
31
+
32
+ function run(cwd: string, args: string[] = []) {
33
+ const result = spawnSync(BASH, [SCRIPT_PATH, ...args], {
34
+ cwd,
35
+ encoding: "utf8",
36
+ env: { ...process.env },
37
+ });
38
+ return result;
39
+ }
40
+
41
+ function git(cwd: string, args: string[]) {
42
+ const result = spawnSync("git", args, { cwd, encoding: "utf8" });
43
+ if (result.status !== 0) {
44
+ throw new Error(
45
+ `git ${args.join(" ")} failed in ${cwd}: ${result.stderr}${result.stdout}`,
46
+ );
47
+ }
48
+ return result.stdout;
49
+ }
50
+
51
+ function initRepoWithCommit(dir: string) {
52
+ git(dir, ["init", "-q", "-b", "main"]);
53
+ git(dir, ["config", "user.email", "test@example.com"]);
54
+ git(dir, ["config", "user.name", "Test"]);
55
+ // Ignore the script's own local-only marker/lock files so creating them
56
+ // doesn't itself count as "dirty" in these throwaway repos.
57
+ writeFileSync(join(dir, ".gitignore"), ".deploy-clone-marker\n.release.lock\n");
58
+ writeFileSync(join(dir, "README.md"), "test repo\n");
59
+ git(dir, ["add", "."]);
60
+ git(dir, ["commit", "-q", "-m", "initial commit"]);
61
+ }
62
+
63
+ function addMarker(dir: string) {
64
+ writeFileSync(join(dir, ".deploy-clone-marker"), "test marker\n");
65
+ }
66
+
67
+ describe("scripts/release.sh guards", () => {
68
+ let workDir: string;
69
+
70
+ beforeAll(() => {
71
+ workDir = mkdtempSync(join(tmpdir(), "release-sh-test-"));
72
+ });
73
+
74
+ it("refuses to run outside the deploy clone (no marker file)", () => {
75
+ const dir = mkdtempSync(join(workDir, "no-marker-"));
76
+ initRepoWithCommit(dir);
77
+ // deliberately no .deploy-clone-marker
78
+
79
+ const result = run(dir);
80
+
81
+ expect(result.status).not.toBe(0);
82
+ expect(result.stderr).toMatch(/not the sanctioned deploy clone/i);
83
+ });
84
+
85
+ it("refuses a dirty working tree even with the marker present", () => {
86
+ const dir = mkdtempSync(join(workDir, "dirty-"));
87
+ initRepoWithCommit(dir);
88
+ addMarker(dir);
89
+ // Make an uncommitted, non-ignored change.
90
+ writeFileSync(join(dir, "README.md"), "modified, uncommitted\n");
91
+
92
+ const result = run(dir);
93
+
94
+ expect(result.status).not.toBe(0);
95
+ expect(result.stderr).toMatch(/dirty/i);
96
+ });
97
+
98
+ it("refuses to run when the lock file is already held", () => {
99
+ const dir = mkdtempSync(join(workDir, "locked-"));
100
+ initRepoWithCommit(dir);
101
+ addMarker(dir);
102
+ writeFileSync(join(dir, ".release.lock"), "99999\n");
103
+
104
+ const result = run(dir);
105
+
106
+ expect(result.status).not.toBe(0);
107
+ expect(result.stderr).toMatch(/lock file .* already exists/i);
108
+ // The guard must not have deleted a lock file it didn't create itself
109
+ // (the trap only fires once *this* run successfully takes the lock).
110
+ expect(existsSync(join(dir, ".release.lock"))).toBe(true);
111
+ rmSync(join(dir, ".release.lock"));
112
+ });
113
+
114
+ it("refuses a target commit that is not reachable from origin/main", () => {
115
+ // A bare "origin" with just the initial commit on main.
116
+ const originDir = mkdtempSync(join(workDir, "origin-"));
117
+ git(originDir, ["init", "-q", "--bare", "-b", "main"]);
118
+
119
+ const seedDir = mkdtempSync(join(workDir, "seed-"));
120
+ initRepoWithCommit(seedDir);
121
+ git(seedDir, ["remote", "add", "origin", originDir]);
122
+ git(seedDir, ["push", "-q", "origin", "main"]);
123
+
124
+ // The deploy clone: clone of origin, plus a local commit on a throwaway
125
+ // branch that is never pushed, so it is not an ancestor of origin/main.
126
+ // main itself stays exactly at origin/main's tip, so the tree the
127
+ // guards see is clean.
128
+ const cloneDir = join(workDir, "clone-unreachable");
129
+ git(workDir, ["clone", "-q", originDir, cloneDir]);
130
+ addMarker(cloneDir);
131
+ git(cloneDir, ["checkout", "-q", "-b", "throwaway"]);
132
+ writeFileSync(join(cloneDir, "unmerged.txt"), "local-only change\n");
133
+ git(cloneDir, ["add", "unmerged.txt"]);
134
+ git(cloneDir, ["commit", "-q", "-m", "unmerged local commit"]);
135
+ const unmergedSha = git(cloneDir, ["rev-parse", "HEAD"]).trim();
136
+ git(cloneDir, ["checkout", "-q", "main"]);
137
+
138
+ const result = run(cloneDir, [unmergedSha]);
139
+
140
+ expect(result.status).not.toBe(0);
141
+ expect(result.stderr).toMatch(/not reachable from origin\/main/i);
142
+ });
143
+ });
144
+
145
+ // scripts/pending-release-count.mjs decides "version + publish" vs "no-op".
146
+ // A release runs from a clone of main, so pending changesets are already on
147
+ // origin/main. The first real release (AK-13, 2026-09-24) counted zero because
148
+ // it asked `changeset status --since=origin/main`; these tests pin the fix by
149
+ // reproducing that exact shape: changesets committed AND pushed.
150
+ describe("scripts/pending-release-count.mjs", () => {
151
+ const COUNT_SCRIPT = resolve(__dirname, "pending-release-count.mjs");
152
+ let workDir: string;
153
+
154
+ beforeAll(() => {
155
+ workDir = mkdtempSync(join(tmpdir(), "pending-count-test-"));
156
+ });
157
+
158
+ function cloneOfPushedPackage(name: string, changesets: Record<string, string>) {
159
+ const originDir = mkdtempSync(join(workDir, `${name}-origin-`));
160
+ git(originDir, ["init", "-q", "--bare", "-b", "main"]);
161
+
162
+ const seedDir = mkdtempSync(join(workDir, `${name}-seed-`));
163
+ initRepoWithCommit(seedDir);
164
+ writeFileSync(
165
+ join(seedDir, "package.json"),
166
+ JSON.stringify({ name: "@test/pkg", version: "0.3.2" }, null, 2),
167
+ );
168
+ mkdirSync(join(seedDir, ".changeset"));
169
+ writeFileSync(
170
+ join(seedDir, ".changeset", "config.json"),
171
+ JSON.stringify({ changelog: false, commit: false, baseBranch: "main", access: "public" }),
172
+ );
173
+ for (const [file, body] of Object.entries(changesets)) {
174
+ writeFileSync(join(seedDir, ".changeset", file), body);
175
+ }
176
+ git(seedDir, ["add", "."]);
177
+ git(seedDir, ["commit", "-q", "-m", "package with changesets"]);
178
+ git(seedDir, ["remote", "add", "origin", originDir]);
179
+ git(seedDir, ["push", "-q", "origin", "main"]);
180
+
181
+ const cloneDir = join(workDir, `${name}-clone`);
182
+ git(workDir, ["clone", "-q", originDir, cloneDir]);
183
+ return cloneDir;
184
+ }
185
+
186
+ function count(cwd: string) {
187
+ const result = spawnSync(process.execPath, [COUNT_SCRIPT], { cwd, encoding: "utf8" });
188
+ if (result.status !== 0) throw new Error(result.stderr || result.stdout);
189
+ return result.stdout;
190
+ }
191
+
192
+ it("counts changesets that are already on origin/main", () => {
193
+ const clone = cloneOfPushedPackage("pending", {
194
+ "minor-feature.md": '---\n"@test/pkg": minor\n---\n\nA feature.\n',
195
+ });
196
+
197
+ expect(count(clone)).toBe("1");
198
+ });
199
+
200
+ it("reports zero when no changesets are pending", () => {
201
+ const clone = cloneOfPushedPackage("none", {});
202
+
203
+ expect(count(clone)).toBe("0");
204
+ });
205
+ });