zinkee 0.1.44 → 0.1.45
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/README.md +4 -0
- package/dist/chunk-MHEVRMDX.js +1230 -0
- package/dist/chunk-MHEVRMDX.js.map +1 -0
- package/{src/utils/examples.ts → dist/examples-E6FFEVBE.js} +856 -1141
- package/dist/examples-E6FFEVBE.js.map +1 -0
- package/dist/index.js +1145 -8456
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
- package/.github/workflows/npm-publish.yml +0 -77
- package/.github/workflows/pr-checks.yml +0 -36
- package/AGENTS.md +0 -110
- package/docs/cli-contract.md +0 -41
- package/docs/npm-release.md +0 -64
- package/docs/superpowers/plans/2026-03-24-zinkee-cli-implementation.md +0 -837
- package/docs/superpowers/plans/2026-03-25-cli-backend-error-contract.md +0 -503
- package/docs/superpowers/plans/2026-03-26-display-freeform-create.md +0 -389
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-backend-blockers.md +0 -172
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-design.md +0 -1576
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-e2e-checklist.md +0 -215
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-e2e-design.md +0 -492
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-e2e-status.md +0 -307
- package/docs/superpowers/specs/2026-07-30-cli-pr-checks-design.md +0 -37
- package/src/api/automations.ts +0 -404
- package/src/api/comments.ts +0 -51
- package/src/api/displays.ts +0 -337
- package/src/api/document-templates.ts +0 -110
- package/src/api/files.ts +0 -50
- package/src/api/formulas.test.ts +0 -65
- package/src/api/formulas.ts +0 -67
- package/src/api/logs.ts +0 -34
- package/src/api/navigation.ts +0 -70
- package/src/api/records.ts +0 -184
- package/src/api/schemas.ts +0 -148
- package/src/api/teamspace.ts +0 -110
- package/src/cli-examples.ts +0 -130
- package/src/cli-runner.ts +0 -95
- package/src/client.test.ts +0 -189
- package/src/client.ts +0 -269
- package/src/command-registry.ts +0 -882
- package/src/commands/automations.test.ts +0 -1030
- package/src/commands/automations.ts +0 -2102
- package/src/commands/comments.test.ts +0 -214
- package/src/commands/comments.ts +0 -303
- package/src/commands/config.test.ts +0 -81
- package/src/commands/config.ts +0 -150
- package/src/commands/displays.test.ts +0 -1105
- package/src/commands/displays.ts +0 -1442
- package/src/commands/document-templates.test.ts +0 -569
- package/src/commands/document-templates.ts +0 -563
- package/src/commands/files.test.ts +0 -284
- package/src/commands/files.ts +0 -280
- package/src/commands/formulas.test.ts +0 -194
- package/src/commands/formulas.ts +0 -243
- package/src/commands/logs.test.ts +0 -123
- package/src/commands/logs.ts +0 -159
- package/src/commands/navigation.test.ts +0 -211
- package/src/commands/navigation.ts +0 -348
- package/src/commands/profiles.test.ts +0 -191
- package/src/commands/profiles.ts +0 -303
- package/src/commands/records.test.ts +0 -860
- package/src/commands/records.ts +0 -883
- package/src/commands/schemas.test.ts +0 -1252
- package/src/commands/schemas.ts +0 -890
- package/src/commands/teamspace.test.ts +0 -229
- package/src/commands/teamspace.ts +0 -546
- package/src/completion/engine.test.ts +0 -138
- package/src/completion/engine.ts +0 -168
- package/src/completion/install.test.ts +0 -179
- package/src/completion/install.ts +0 -260
- package/src/completion/runtime.ts +0 -150
- package/src/completion/scripts.ts +0 -91
- package/src/config.test.ts +0 -362
- package/src/config.ts +0 -294
- package/src/index.test.ts +0 -217
- package/src/index.ts +0 -8
- package/src/parsers/expressions.test.ts +0 -95
- package/src/parsers/expressions.ts +0 -128
- package/src/parsers/kv.test.ts +0 -35
- package/src/parsers/kv.ts +0 -49
- package/src/parsers/selectors.test.ts +0 -23
- package/src/parsers/selectors.ts +0 -29
- package/src/program.ts +0 -64
- package/src/runtime-context.ts +0 -103
- package/src/types.ts +0 -76
- package/src/utils/argv-rewrite.test.ts +0 -149
- package/src/utils/argv-rewrite.ts +0 -269
- package/src/utils/errors.test.ts +0 -85
- package/src/utils/errors.ts +0 -191
- package/src/utils/examples.test.ts +0 -374
- package/src/utils/output.test.ts +0 -65
- package/src/utils/output.ts +0 -154
- package/src/utils/schema-fields.ts +0 -1016
- package/tsconfig.json +0 -20
- package/tsup.config.ts +0 -13
|
@@ -1,569 +0,0 @@
|
|
|
1
|
-
import { mkdtempSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
|
|
5
|
-
import { Command } from "commander";
|
|
6
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
7
|
-
|
|
8
|
-
import { registerDocumentTemplatesCommands } from "./document-templates.js";
|
|
9
|
-
|
|
10
|
-
const TEMPLATE_ID = "7c4a8d3f-1b2e-4a5c-9d6e-1f2a3b4c5d6e";
|
|
11
|
-
const ASSET_ID = "9e8d7c6b-5a4f-3e2d-1c0b-9a8b7c6d5e4f";
|
|
12
|
-
|
|
13
|
-
function createTempConfig(contents: string): string {
|
|
14
|
-
const dir = mkdtempSync(join(tmpdir(), "zinkee-document-templates-"));
|
|
15
|
-
const filePath = join(dir, "config.toml");
|
|
16
|
-
writeFileSync(filePath, contents, "utf8");
|
|
17
|
-
return filePath;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function createApiConfig(): string {
|
|
21
|
-
return createTempConfig(`
|
|
22
|
-
default_profile = "prod"
|
|
23
|
-
|
|
24
|
-
[profiles.prod]
|
|
25
|
-
api_key = "document-templates-token-1234"
|
|
26
|
-
base_url = "https://api.zinkee.test"
|
|
27
|
-
`);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function createStdoutBuffer() {
|
|
31
|
-
const chunks: Buffer[] = [];
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
stdout: {
|
|
35
|
-
write(chunk: string | Uint8Array) {
|
|
36
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
37
|
-
return true;
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
readText() {
|
|
41
|
-
return Buffer.concat(chunks).toString("utf8");
|
|
42
|
-
},
|
|
43
|
-
readBytes() {
|
|
44
|
-
return Buffer.concat(chunks);
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function jsonResponse(body: unknown, status: number = 200): Response {
|
|
50
|
-
return new Response(JSON.stringify(body), {
|
|
51
|
-
status,
|
|
52
|
-
headers: { "content-type": "application/json" },
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function binaryResponse(bytes: number[], status: number = 200): Response {
|
|
57
|
-
return new Response(new Uint8Array(bytes), {
|
|
58
|
-
status,
|
|
59
|
-
headers: { "content-type": "application/octet-stream" },
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function createFetchMock() {
|
|
64
|
-
const fetchMock = vi.fn<typeof fetch>();
|
|
65
|
-
vi.stubGlobal("fetch", fetchMock);
|
|
66
|
-
return fetchMock;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function createProgram(): Command {
|
|
70
|
-
return new Command()
|
|
71
|
-
.enablePositionalOptions()
|
|
72
|
-
.option("--profile <name>", "select a profile from config")
|
|
73
|
-
.option("--base-url <url>", "override the configured base URL")
|
|
74
|
-
.option("--api-key <token>", "override the configured API key")
|
|
75
|
-
.option("--read-only", "run in read-only mode")
|
|
76
|
-
.option("--json", "emit JSON output");
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function templateFixture(overrides: Record<string, unknown> = {}): Record<string, unknown> {
|
|
80
|
-
return {
|
|
81
|
-
id: TEMPLATE_ID,
|
|
82
|
-
name: "Quarterly report",
|
|
83
|
-
assets: [
|
|
84
|
-
{
|
|
85
|
-
id: ASSET_ID,
|
|
86
|
-
name: "Requirements.docx",
|
|
87
|
-
link: `/api/v2/document-templates/${TEMPLATE_ID}/assets/${ASSET_ID}/download`,
|
|
88
|
-
},
|
|
89
|
-
],
|
|
90
|
-
createdAt: "2026-01-15T10:00:00Z",
|
|
91
|
-
updatedAt: "2026-01-20T14:30:00Z",
|
|
92
|
-
createdBy: "creator@example.com",
|
|
93
|
-
updatedBy: "updater@example.com",
|
|
94
|
-
...overrides,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
afterEach(() => {
|
|
99
|
-
vi.unstubAllGlobals();
|
|
100
|
-
vi.restoreAllMocks();
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
describe("document-templates commands", () => {
|
|
104
|
-
it("registers the document-templates command group", () => {
|
|
105
|
-
const program = createProgram();
|
|
106
|
-
registerDocumentTemplatesCommands(program);
|
|
107
|
-
|
|
108
|
-
expect(program.commands.some((command) => command.name() === "document-templates")).toBe(true);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it("lists document templates as JSON", async () => {
|
|
112
|
-
const configPath = createApiConfig();
|
|
113
|
-
const output = createStdoutBuffer();
|
|
114
|
-
const fetchMock = createFetchMock();
|
|
115
|
-
fetchMock.mockResolvedValueOnce(jsonResponse([templateFixture()]));
|
|
116
|
-
const program = createProgram();
|
|
117
|
-
registerDocumentTemplatesCommands(program, { configPath, stdout: output.stdout });
|
|
118
|
-
|
|
119
|
-
await program.parseAsync(["node", "zinkee", "--json", "document-templates", "list"]);
|
|
120
|
-
|
|
121
|
-
expect(fetchMock.mock.calls[0]?.[0]).toBe("https://api.zinkee.test/api/v2/document-templates");
|
|
122
|
-
expect(JSON.parse(output.readText())).toEqual({
|
|
123
|
-
data: [templateFixture()],
|
|
124
|
-
meta: { command: "document-templates list", configPath },
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("gets a template by UUID by filtering the list response", async () => {
|
|
129
|
-
const configPath = createApiConfig();
|
|
130
|
-
const output = createStdoutBuffer();
|
|
131
|
-
const fetchMock = createFetchMock();
|
|
132
|
-
const target = templateFixture();
|
|
133
|
-
const other = templateFixture({ id: "11111111-1111-4111-a111-111111111111", name: "Another" });
|
|
134
|
-
fetchMock.mockResolvedValueOnce(jsonResponse([other, target]));
|
|
135
|
-
const program = createProgram();
|
|
136
|
-
registerDocumentTemplatesCommands(program, { configPath, stdout: output.stdout });
|
|
137
|
-
|
|
138
|
-
await program.parseAsync([
|
|
139
|
-
"node",
|
|
140
|
-
"zinkee",
|
|
141
|
-
"--json",
|
|
142
|
-
"document-templates",
|
|
143
|
-
"get",
|
|
144
|
-
TEMPLATE_ID,
|
|
145
|
-
]);
|
|
146
|
-
|
|
147
|
-
expect(JSON.parse(output.readText()).data).toEqual(target);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it("gets a template by exact name", async () => {
|
|
151
|
-
const configPath = createApiConfig();
|
|
152
|
-
const output = createStdoutBuffer();
|
|
153
|
-
const fetchMock = createFetchMock();
|
|
154
|
-
fetchMock.mockResolvedValueOnce(jsonResponse([templateFixture()]));
|
|
155
|
-
const program = createProgram();
|
|
156
|
-
registerDocumentTemplatesCommands(program, { configPath, stdout: output.stdout });
|
|
157
|
-
|
|
158
|
-
await program.parseAsync([
|
|
159
|
-
"node",
|
|
160
|
-
"zinkee",
|
|
161
|
-
"--json",
|
|
162
|
-
"document-templates",
|
|
163
|
-
"get",
|
|
164
|
-
"Quarterly report",
|
|
165
|
-
]);
|
|
166
|
-
|
|
167
|
-
expect(JSON.parse(output.readText()).data.id).toBe(TEMPLATE_ID);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it("fails when the template name is ambiguous", async () => {
|
|
171
|
-
const configPath = createApiConfig();
|
|
172
|
-
const fetchMock = createFetchMock();
|
|
173
|
-
fetchMock.mockResolvedValueOnce(
|
|
174
|
-
jsonResponse([
|
|
175
|
-
templateFixture({ id: "aaaaaaaa-1111-4111-a111-111111111111" }),
|
|
176
|
-
templateFixture({ id: "bbbbbbbb-2222-4222-a222-222222222222" }),
|
|
177
|
-
]),
|
|
178
|
-
);
|
|
179
|
-
const program = createProgram();
|
|
180
|
-
registerDocumentTemplatesCommands(program, { configPath });
|
|
181
|
-
|
|
182
|
-
await expect(
|
|
183
|
-
program.parseAsync([
|
|
184
|
-
"node",
|
|
185
|
-
"zinkee",
|
|
186
|
-
"document-templates",
|
|
187
|
-
"get",
|
|
188
|
-
"Quarterly report",
|
|
189
|
-
]),
|
|
190
|
-
).rejects.toThrowError('Document template "Quarterly report" is ambiguous. Found 2 matches.');
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it("creates a template with an empty assets array by default", async () => {
|
|
194
|
-
const configPath = createApiConfig();
|
|
195
|
-
const output = createStdoutBuffer();
|
|
196
|
-
const fetchMock = createFetchMock();
|
|
197
|
-
fetchMock.mockResolvedValueOnce(jsonResponse(templateFixture({ assets: [] })));
|
|
198
|
-
const program = createProgram();
|
|
199
|
-
registerDocumentTemplatesCommands(program, { configPath, stdout: output.stdout });
|
|
200
|
-
|
|
201
|
-
await program.parseAsync([
|
|
202
|
-
"node",
|
|
203
|
-
"zinkee",
|
|
204
|
-
"--json",
|
|
205
|
-
"document-templates",
|
|
206
|
-
"create",
|
|
207
|
-
"--name",
|
|
208
|
-
"Quarterly report",
|
|
209
|
-
]);
|
|
210
|
-
|
|
211
|
-
const [url, init] = fetchMock.mock.calls[0] ?? [];
|
|
212
|
-
expect(url).toBe("https://api.zinkee.test/api/v2/document-templates");
|
|
213
|
-
expect(init?.method).toBe("POST");
|
|
214
|
-
expect(JSON.parse(String(init?.body))).toEqual({
|
|
215
|
-
name: "Quarterly report",
|
|
216
|
-
assets: [],
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
it("creates a template with pre-existing assets via --raw", async () => {
|
|
221
|
-
const configPath = createApiConfig();
|
|
222
|
-
const fetchMock = createFetchMock();
|
|
223
|
-
fetchMock.mockResolvedValueOnce(jsonResponse(templateFixture()));
|
|
224
|
-
const program = createProgram();
|
|
225
|
-
registerDocumentTemplatesCommands(program, { configPath });
|
|
226
|
-
|
|
227
|
-
const rawPayload = {
|
|
228
|
-
name: "Annual report",
|
|
229
|
-
assets: [{ id: ASSET_ID, name: "Cover.docx", link: "/existing/cover.docx" }],
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
await program.parseAsync([
|
|
233
|
-
"node",
|
|
234
|
-
"zinkee",
|
|
235
|
-
"--json",
|
|
236
|
-
"document-templates",
|
|
237
|
-
"create",
|
|
238
|
-
"--raw",
|
|
239
|
-
JSON.stringify(rawPayload),
|
|
240
|
-
]);
|
|
241
|
-
|
|
242
|
-
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual(rawPayload);
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
it("rejects create without --name", async () => {
|
|
246
|
-
const configPath = createApiConfig();
|
|
247
|
-
const fetchMock = createFetchMock();
|
|
248
|
-
const program = createProgram();
|
|
249
|
-
registerDocumentTemplatesCommands(program, { configPath });
|
|
250
|
-
|
|
251
|
-
await expect(
|
|
252
|
-
program.parseAsync(["node", "zinkee", "document-templates", "create"]),
|
|
253
|
-
).rejects.toThrowError('Provide template name via "--name".');
|
|
254
|
-
|
|
255
|
-
expect(fetchMock).not.toHaveBeenCalled();
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
it("resolves a template by name and patches the name", async () => {
|
|
259
|
-
const configPath = createApiConfig();
|
|
260
|
-
const fetchMock = createFetchMock();
|
|
261
|
-
fetchMock
|
|
262
|
-
.mockResolvedValueOnce(jsonResponse([templateFixture()]))
|
|
263
|
-
.mockResolvedValueOnce(jsonResponse(templateFixture({ name: "Annual report" })));
|
|
264
|
-
const program = createProgram();
|
|
265
|
-
registerDocumentTemplatesCommands(program, { configPath });
|
|
266
|
-
|
|
267
|
-
await program.parseAsync([
|
|
268
|
-
"node",
|
|
269
|
-
"zinkee",
|
|
270
|
-
"--json",
|
|
271
|
-
"document-templates",
|
|
272
|
-
"update",
|
|
273
|
-
"Quarterly report",
|
|
274
|
-
"--name",
|
|
275
|
-
"Annual report",
|
|
276
|
-
]);
|
|
277
|
-
|
|
278
|
-
expect(fetchMock.mock.calls[1]?.[0]).toBe(
|
|
279
|
-
`https://api.zinkee.test/api/v2/document-templates/${TEMPLATE_ID}`,
|
|
280
|
-
);
|
|
281
|
-
expect(fetchMock.mock.calls[1]?.[1]?.method).toBe("PATCH");
|
|
282
|
-
expect(JSON.parse(String(fetchMock.mock.calls[1]?.[1]?.body))).toEqual({
|
|
283
|
-
name: "Annual report",
|
|
284
|
-
});
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
it("rejects update with no fields", async () => {
|
|
288
|
-
const configPath = createApiConfig();
|
|
289
|
-
const fetchMock = createFetchMock();
|
|
290
|
-
const program = createProgram();
|
|
291
|
-
registerDocumentTemplatesCommands(program, { configPath });
|
|
292
|
-
|
|
293
|
-
await expect(
|
|
294
|
-
program.parseAsync([
|
|
295
|
-
"node",
|
|
296
|
-
"zinkee",
|
|
297
|
-
"document-templates",
|
|
298
|
-
"update",
|
|
299
|
-
TEMPLATE_ID,
|
|
300
|
-
]),
|
|
301
|
-
).rejects.toThrowError('Provide template updates via "--name".');
|
|
302
|
-
|
|
303
|
-
expect(fetchMock).not.toHaveBeenCalled();
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
it("deletes a template by UUID", async () => {
|
|
307
|
-
const configPath = createApiConfig();
|
|
308
|
-
const output = createStdoutBuffer();
|
|
309
|
-
const fetchMock = createFetchMock();
|
|
310
|
-
fetchMock.mockResolvedValueOnce(jsonResponse({ id: TEMPLATE_ID, deleted: true }));
|
|
311
|
-
const program = createProgram();
|
|
312
|
-
registerDocumentTemplatesCommands(program, { configPath, stdout: output.stdout });
|
|
313
|
-
|
|
314
|
-
await program.parseAsync([
|
|
315
|
-
"node",
|
|
316
|
-
"zinkee",
|
|
317
|
-
"--json",
|
|
318
|
-
"document-templates",
|
|
319
|
-
"delete",
|
|
320
|
-
TEMPLATE_ID,
|
|
321
|
-
]);
|
|
322
|
-
|
|
323
|
-
expect(fetchMock.mock.calls[0]?.[0]).toBe(
|
|
324
|
-
`https://api.zinkee.test/api/v2/document-templates/${TEMPLATE_ID}`,
|
|
325
|
-
);
|
|
326
|
-
expect(fetchMock.mock.calls[0]?.[1]?.method).toBe("DELETE");
|
|
327
|
-
expect(JSON.parse(output.readText()).data).toEqual({ id: TEMPLATE_ID, deleted: true });
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
it("adds an asset through multipart with --name override", async () => {
|
|
331
|
-
const configPath = createApiConfig();
|
|
332
|
-
const fetchMock = createFetchMock();
|
|
333
|
-
fetchMock.mockResolvedValueOnce(jsonResponse(templateFixture()));
|
|
334
|
-
const program = createProgram();
|
|
335
|
-
registerDocumentTemplatesCommands(program, { configPath });
|
|
336
|
-
|
|
337
|
-
const tempDir = mkdtempSync(join(tmpdir(), "zinkee-document-templates-upload-"));
|
|
338
|
-
const localPath = join(tempDir, "contract.docx");
|
|
339
|
-
writeFileSync(localPath, "binary-bytes", "utf8");
|
|
340
|
-
|
|
341
|
-
await program.parseAsync([
|
|
342
|
-
"node",
|
|
343
|
-
"zinkee",
|
|
344
|
-
"--json",
|
|
345
|
-
"document-templates",
|
|
346
|
-
"assets",
|
|
347
|
-
"add",
|
|
348
|
-
TEMPLATE_ID,
|
|
349
|
-
localPath,
|
|
350
|
-
"--name",
|
|
351
|
-
"Customer Contract",
|
|
352
|
-
]);
|
|
353
|
-
|
|
354
|
-
const [url, init] = fetchMock.mock.calls[0] ?? [];
|
|
355
|
-
expect(url).toBe(
|
|
356
|
-
`https://api.zinkee.test/api/v2/document-templates/${TEMPLATE_ID}/assets`,
|
|
357
|
-
);
|
|
358
|
-
expect(init?.method).toBe("POST");
|
|
359
|
-
|
|
360
|
-
const body = init?.body as FormData;
|
|
361
|
-
const filePart = body.get("file");
|
|
362
|
-
expect(filePart).toBeInstanceOf(File);
|
|
363
|
-
if (!(filePart instanceof File)) {
|
|
364
|
-
throw new Error("Expected the asset upload to send a File.");
|
|
365
|
-
}
|
|
366
|
-
expect(filePart.name).toBe("contract.docx");
|
|
367
|
-
expect(Buffer.from(await filePart.arrayBuffer()).toString("utf8")).toBe("binary-bytes");
|
|
368
|
-
expect(body.get("name")).toBe("Customer Contract");
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
it("does not append --name when not provided", async () => {
|
|
372
|
-
const configPath = createApiConfig();
|
|
373
|
-
const fetchMock = createFetchMock();
|
|
374
|
-
fetchMock.mockResolvedValueOnce(jsonResponse(templateFixture()));
|
|
375
|
-
const program = createProgram();
|
|
376
|
-
registerDocumentTemplatesCommands(program, { configPath });
|
|
377
|
-
|
|
378
|
-
const tempDir = mkdtempSync(join(tmpdir(), "zinkee-document-templates-upload-"));
|
|
379
|
-
const localPath = join(tempDir, "contract.docx");
|
|
380
|
-
writeFileSync(localPath, "binary-bytes", "utf8");
|
|
381
|
-
|
|
382
|
-
await program.parseAsync([
|
|
383
|
-
"node",
|
|
384
|
-
"zinkee",
|
|
385
|
-
"document-templates",
|
|
386
|
-
"assets",
|
|
387
|
-
"add",
|
|
388
|
-
TEMPLATE_ID,
|
|
389
|
-
localPath,
|
|
390
|
-
]);
|
|
391
|
-
|
|
392
|
-
const body = fetchMock.mock.calls[0]?.[1]?.body as FormData;
|
|
393
|
-
expect(body.get("name")).toBeNull();
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
it("deletes one asset", async () => {
|
|
397
|
-
const configPath = createApiConfig();
|
|
398
|
-
const fetchMock = createFetchMock();
|
|
399
|
-
fetchMock.mockResolvedValueOnce(jsonResponse({ id: ASSET_ID, deleted: true }));
|
|
400
|
-
const program = createProgram();
|
|
401
|
-
registerDocumentTemplatesCommands(program, { configPath });
|
|
402
|
-
|
|
403
|
-
await program.parseAsync([
|
|
404
|
-
"node",
|
|
405
|
-
"zinkee",
|
|
406
|
-
"document-templates",
|
|
407
|
-
"assets",
|
|
408
|
-
"delete",
|
|
409
|
-
TEMPLATE_ID,
|
|
410
|
-
ASSET_ID,
|
|
411
|
-
]);
|
|
412
|
-
|
|
413
|
-
expect(fetchMock.mock.calls[0]?.[0]).toBe(
|
|
414
|
-
`https://api.zinkee.test/api/v2/document-templates/${TEMPLATE_ID}/assets/${ASSET_ID}`,
|
|
415
|
-
);
|
|
416
|
-
expect(fetchMock.mock.calls[0]?.[1]?.method).toBe("DELETE");
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
it("downloads asset bytes to stdout when no --output is provided", async () => {
|
|
420
|
-
const configPath = createApiConfig();
|
|
421
|
-
const output = createStdoutBuffer();
|
|
422
|
-
const fetchMock = createFetchMock();
|
|
423
|
-
fetchMock.mockResolvedValueOnce(binaryResponse([104, 101, 108, 108, 111]));
|
|
424
|
-
const program = createProgram();
|
|
425
|
-
registerDocumentTemplatesCommands(program, { configPath, stdout: output.stdout });
|
|
426
|
-
|
|
427
|
-
await program.parseAsync([
|
|
428
|
-
"node",
|
|
429
|
-
"zinkee",
|
|
430
|
-
"document-templates",
|
|
431
|
-
"assets",
|
|
432
|
-
"download",
|
|
433
|
-
TEMPLATE_ID,
|
|
434
|
-
ASSET_ID,
|
|
435
|
-
]);
|
|
436
|
-
|
|
437
|
-
expect(fetchMock.mock.calls[0]?.[0]).toBe(
|
|
438
|
-
`https://api.zinkee.test/api/v2/document-templates/${TEMPLATE_ID}/assets/${ASSET_ID}/download`,
|
|
439
|
-
);
|
|
440
|
-
expect(output.readBytes().equals(Buffer.from("hello"))).toBe(true);
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
it("downloads asset bytes to disk and emits a JSON envelope", async () => {
|
|
444
|
-
const configPath = createApiConfig();
|
|
445
|
-
const output = createStdoutBuffer();
|
|
446
|
-
const fetchMock = createFetchMock();
|
|
447
|
-
fetchMock.mockResolvedValueOnce(binaryResponse([1, 2, 3]));
|
|
448
|
-
const tempDir = mkdtempSync(join(tmpdir(), "zinkee-document-templates-download-"));
|
|
449
|
-
const outputPath = join(tempDir, "downloaded.bin");
|
|
450
|
-
const program = createProgram();
|
|
451
|
-
registerDocumentTemplatesCommands(program, { configPath, stdout: output.stdout });
|
|
452
|
-
|
|
453
|
-
await program.parseAsync([
|
|
454
|
-
"node",
|
|
455
|
-
"zinkee",
|
|
456
|
-
"--json",
|
|
457
|
-
"document-templates",
|
|
458
|
-
"assets",
|
|
459
|
-
"download",
|
|
460
|
-
TEMPLATE_ID,
|
|
461
|
-
ASSET_ID,
|
|
462
|
-
"--output",
|
|
463
|
-
outputPath,
|
|
464
|
-
]);
|
|
465
|
-
|
|
466
|
-
expect(readFileSync(outputPath)).toEqual(Buffer.from([1, 2, 3]));
|
|
467
|
-
expect(JSON.parse(output.readText())).toEqual({
|
|
468
|
-
data: {
|
|
469
|
-
template: TEMPLATE_ID,
|
|
470
|
-
asset: ASSET_ID,
|
|
471
|
-
output: outputPath,
|
|
472
|
-
bytes: 3,
|
|
473
|
-
},
|
|
474
|
-
meta: { command: "document-templates assets download", configPath },
|
|
475
|
-
});
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
it("requires --output when downloading with --json", async () => {
|
|
479
|
-
const configPath = createApiConfig();
|
|
480
|
-
const fetchMock = createFetchMock();
|
|
481
|
-
const program = createProgram();
|
|
482
|
-
registerDocumentTemplatesCommands(program, { configPath });
|
|
483
|
-
|
|
484
|
-
await expect(
|
|
485
|
-
program.parseAsync([
|
|
486
|
-
"node",
|
|
487
|
-
"zinkee",
|
|
488
|
-
"--json",
|
|
489
|
-
"document-templates",
|
|
490
|
-
"assets",
|
|
491
|
-
"download",
|
|
492
|
-
TEMPLATE_ID,
|
|
493
|
-
ASSET_ID,
|
|
494
|
-
]),
|
|
495
|
-
).rejects.toThrowError(
|
|
496
|
-
'Provide "--output" when using "--json" with "document-templates assets download".',
|
|
497
|
-
);
|
|
498
|
-
|
|
499
|
-
expect(fetchMock).not.toHaveBeenCalled();
|
|
500
|
-
});
|
|
501
|
-
|
|
502
|
-
it("blocks create in read-only mode", async () => {
|
|
503
|
-
const configPath = createApiConfig();
|
|
504
|
-
const fetchMock = createFetchMock();
|
|
505
|
-
const program = createProgram();
|
|
506
|
-
registerDocumentTemplatesCommands(program, { configPath });
|
|
507
|
-
|
|
508
|
-
await expect(
|
|
509
|
-
program.parseAsync([
|
|
510
|
-
"node",
|
|
511
|
-
"zinkee",
|
|
512
|
-
"--read-only",
|
|
513
|
-
"document-templates",
|
|
514
|
-
"create",
|
|
515
|
-
"--name",
|
|
516
|
-
"Quarterly report",
|
|
517
|
-
]),
|
|
518
|
-
).rejects.toThrowError('Command "document-templates create" is not allowed in read-only mode.');
|
|
519
|
-
|
|
520
|
-
expect(fetchMock).not.toHaveBeenCalled();
|
|
521
|
-
});
|
|
522
|
-
|
|
523
|
-
it("blocks delete in read-only mode", async () => {
|
|
524
|
-
const configPath = createApiConfig();
|
|
525
|
-
const fetchMock = createFetchMock();
|
|
526
|
-
const program = createProgram();
|
|
527
|
-
registerDocumentTemplatesCommands(program, { configPath });
|
|
528
|
-
|
|
529
|
-
await expect(
|
|
530
|
-
program.parseAsync([
|
|
531
|
-
"node",
|
|
532
|
-
"zinkee",
|
|
533
|
-
"--read-only",
|
|
534
|
-
"document-templates",
|
|
535
|
-
"delete",
|
|
536
|
-
TEMPLATE_ID,
|
|
537
|
-
]),
|
|
538
|
-
).rejects.toThrowError('Command "document-templates delete" is not allowed in read-only mode.');
|
|
539
|
-
|
|
540
|
-
expect(fetchMock).not.toHaveBeenCalled();
|
|
541
|
-
});
|
|
542
|
-
|
|
543
|
-
it("blocks asset uploads in read-only mode", async () => {
|
|
544
|
-
const configPath = createApiConfig();
|
|
545
|
-
const fetchMock = createFetchMock();
|
|
546
|
-
const tempDir = mkdtempSync(join(tmpdir(), "zinkee-document-templates-upload-ro-"));
|
|
547
|
-
const localPath = join(tempDir, "contract.docx");
|
|
548
|
-
writeFileSync(localPath, "binary-bytes", "utf8");
|
|
549
|
-
const program = createProgram();
|
|
550
|
-
registerDocumentTemplatesCommands(program, { configPath });
|
|
551
|
-
|
|
552
|
-
await expect(
|
|
553
|
-
program.parseAsync([
|
|
554
|
-
"node",
|
|
555
|
-
"zinkee",
|
|
556
|
-
"--read-only",
|
|
557
|
-
"document-templates",
|
|
558
|
-
"assets",
|
|
559
|
-
"add",
|
|
560
|
-
TEMPLATE_ID,
|
|
561
|
-
localPath,
|
|
562
|
-
]),
|
|
563
|
-
).rejects.toThrowError(
|
|
564
|
-
'Command "document-templates assets add" is not allowed in read-only mode.',
|
|
565
|
-
);
|
|
566
|
-
|
|
567
|
-
expect(fetchMock).not.toHaveBeenCalled();
|
|
568
|
-
});
|
|
569
|
-
});
|