@getjack/jack 0.1.4 → 0.1.6
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/package.json +2 -6
- package/src/commands/agents.ts +9 -24
- package/src/commands/clone.ts +27 -0
- package/src/commands/down.ts +31 -57
- package/src/commands/feedback.ts +4 -5
- package/src/commands/link.ts +147 -0
- package/src/commands/login.ts +124 -1
- package/src/commands/logs.ts +8 -18
- package/src/commands/new.ts +7 -1
- package/src/commands/projects.ts +166 -105
- package/src/commands/secrets.ts +7 -6
- package/src/commands/services.ts +5 -4
- package/src/commands/tag.ts +282 -0
- package/src/commands/unlink.ts +30 -0
- package/src/index.ts +46 -1
- package/src/lib/auth/index.ts +2 -0
- package/src/lib/auth/store.ts +26 -2
- package/src/lib/binding-validator.ts +4 -13
- package/src/lib/build-helper.ts +93 -5
- package/src/lib/control-plane.ts +137 -0
- package/src/lib/deploy-mode.ts +1 -1
- package/src/lib/managed-deploy.ts +11 -1
- package/src/lib/managed-down.ts +7 -20
- package/src/lib/paths-index.test.ts +546 -0
- package/src/lib/paths-index.ts +310 -0
- package/src/lib/project-link.test.ts +459 -0
- package/src/lib/project-link.ts +279 -0
- package/src/lib/project-list.test.ts +581 -0
- package/src/lib/project-list.ts +449 -0
- package/src/lib/project-operations.ts +304 -183
- package/src/lib/project-resolver.ts +191 -211
- package/src/lib/tags.ts +389 -0
- package/src/lib/telemetry.ts +86 -157
- package/src/lib/zip-packager.ts +9 -0
- package/src/templates/index.ts +5 -3
- package/templates/api/.jack/template.json +4 -0
- package/templates/hello/.jack/template.json +4 -0
- package/templates/miniapp/.jack/template.json +4 -0
- package/templates/nextjs/.jack.json +28 -0
- package/templates/nextjs/app/globals.css +9 -0
- package/templates/nextjs/app/layout.tsx +19 -0
- package/templates/nextjs/app/page.tsx +8 -0
- package/templates/nextjs/bun.lock +2232 -0
- package/templates/nextjs/cloudflare-env.d.ts +3 -0
- package/templates/nextjs/next-env.d.ts +6 -0
- package/templates/nextjs/next.config.ts +8 -0
- package/templates/nextjs/open-next.config.ts +6 -0
- package/templates/nextjs/package.json +24 -0
- package/templates/nextjs/public/_headers +2 -0
- package/templates/nextjs/tsconfig.json +44 -0
- package/templates/nextjs/wrangler.jsonc +17 -0
- package/src/lib/local-paths.test.ts +0 -902
- package/src/lib/local-paths.ts +0 -258
- package/src/lib/registry.ts +0 -181
package/src/lib/control-plane.ts
CHANGED
|
@@ -40,6 +40,7 @@ export interface CreateProjectResponse {
|
|
|
40
40
|
status?: "live" | "created";
|
|
41
41
|
url?: string;
|
|
42
42
|
prebuilt_failed?: boolean;
|
|
43
|
+
prebuilt_error?: string;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
export interface SlugAvailabilityResponse {
|
|
@@ -48,6 +49,27 @@ export interface SlugAvailabilityResponse {
|
|
|
48
49
|
error?: string;
|
|
49
50
|
}
|
|
50
51
|
|
|
52
|
+
export interface UsernameAvailabilityResponse {
|
|
53
|
+
available: boolean;
|
|
54
|
+
username: string;
|
|
55
|
+
error?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface SetUsernameResponse {
|
|
59
|
+
success: boolean;
|
|
60
|
+
username: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface UserProfile {
|
|
64
|
+
id: string;
|
|
65
|
+
email: string;
|
|
66
|
+
first_name: string | null;
|
|
67
|
+
last_name: string | null;
|
|
68
|
+
username: string | null;
|
|
69
|
+
created_at: string;
|
|
70
|
+
updated_at: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
51
73
|
export interface CreateDeploymentRequest {
|
|
52
74
|
source: string;
|
|
53
75
|
}
|
|
@@ -164,6 +186,7 @@ export interface ManagedProject {
|
|
|
164
186
|
status: "active" | "error" | "deleted";
|
|
165
187
|
created_at: string;
|
|
166
188
|
updated_at: string;
|
|
189
|
+
tags?: string; // JSON string array from DB, e.g., '["backend", "api"]'
|
|
167
190
|
}
|
|
168
191
|
|
|
169
192
|
/**
|
|
@@ -293,3 +316,117 @@ export async function fetchProjectResources(projectId: string): Promise<ProjectR
|
|
|
293
316
|
const data = (await response.json()) as { resources: ProjectResource[] };
|
|
294
317
|
return data.resources;
|
|
295
318
|
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Sync project tags to the control plane.
|
|
322
|
+
* Fire-and-forget: errors are logged but not thrown.
|
|
323
|
+
*/
|
|
324
|
+
export async function syncProjectTags(projectId: string, tags: string[]): Promise<void> {
|
|
325
|
+
const { authFetch } = await import("./auth/index.ts");
|
|
326
|
+
const { debug } = await import("./debug.ts");
|
|
327
|
+
|
|
328
|
+
try {
|
|
329
|
+
const response = await authFetch(`${getControlApiUrl()}/v1/projects/${projectId}/tags`, {
|
|
330
|
+
method: "PUT",
|
|
331
|
+
headers: { "Content-Type": "application/json" },
|
|
332
|
+
body: JSON.stringify({ tags }),
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
if (!response.ok) {
|
|
336
|
+
// Log but don't throw - tag sync is non-critical
|
|
337
|
+
debug(`Tag sync failed: ${response.status}`);
|
|
338
|
+
}
|
|
339
|
+
} catch (error) {
|
|
340
|
+
// Log but don't throw - tag sync is non-critical
|
|
341
|
+
debug(`Tag sync failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Fetch project tags from the control plane.
|
|
347
|
+
* Returns empty array on error.
|
|
348
|
+
*/
|
|
349
|
+
export async function fetchProjectTags(projectId: string): Promise<string[]> {
|
|
350
|
+
const { authFetch } = await import("./auth/index.ts");
|
|
351
|
+
|
|
352
|
+
try {
|
|
353
|
+
const response = await authFetch(`${getControlApiUrl()}/v1/projects/${projectId}/tags`);
|
|
354
|
+
|
|
355
|
+
if (!response.ok) {
|
|
356
|
+
return [];
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const data = (await response.json()) as { tags: string[] };
|
|
360
|
+
return data.tags ?? [];
|
|
361
|
+
} catch {
|
|
362
|
+
return [];
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Check if a username is available on jack cloud.
|
|
368
|
+
* Does not require authentication.
|
|
369
|
+
*/
|
|
370
|
+
export async function checkUsernameAvailable(
|
|
371
|
+
username: string,
|
|
372
|
+
): Promise<UsernameAvailabilityResponse> {
|
|
373
|
+
const response = await fetch(
|
|
374
|
+
`${getControlApiUrl()}/v1/usernames/${encodeURIComponent(username)}/available`,
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
if (!response.ok) {
|
|
378
|
+
throw new Error(`Failed to check username availability: ${response.status}`);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return response.json() as Promise<UsernameAvailabilityResponse>;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Set the current user's username.
|
|
386
|
+
* Can only be called once per user.
|
|
387
|
+
*/
|
|
388
|
+
export async function setUsername(username: string): Promise<SetUsernameResponse> {
|
|
389
|
+
const { authFetch } = await import("./auth/index.ts");
|
|
390
|
+
|
|
391
|
+
const response = await authFetch(`${getControlApiUrl()}/v1/me/username`, {
|
|
392
|
+
method: "PUT",
|
|
393
|
+
headers: { "Content-Type": "application/json" },
|
|
394
|
+
body: JSON.stringify({ username }),
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
if (response.status === 409) {
|
|
398
|
+
const err = (await response.json().catch(() => ({ message: "Username taken" }))) as {
|
|
399
|
+
message?: string;
|
|
400
|
+
};
|
|
401
|
+
throw new Error(err.message || "Username is already taken");
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (!response.ok) {
|
|
405
|
+
const err = (await response.json().catch(() => ({ message: "Unknown error" }))) as {
|
|
406
|
+
message?: string;
|
|
407
|
+
};
|
|
408
|
+
throw new Error(err.message || `Failed to set username: ${response.status}`);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return response.json() as Promise<SetUsernameResponse>;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Get the current user's profile including username.
|
|
416
|
+
*/
|
|
417
|
+
export async function getCurrentUserProfile(): Promise<UserProfile | null> {
|
|
418
|
+
const { authFetch } = await import("./auth/index.ts");
|
|
419
|
+
|
|
420
|
+
try {
|
|
421
|
+
const response = await authFetch(`${getControlApiUrl()}/v1/me`);
|
|
422
|
+
|
|
423
|
+
if (!response.ok) {
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const data = (await response.json()) as { user: UserProfile };
|
|
428
|
+
return data.user;
|
|
429
|
+
} catch {
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
}
|
package/src/lib/deploy-mode.ts
CHANGED
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
import { validateBindings } from "./binding-validator.ts";
|
|
8
8
|
import { buildProject, parseWranglerConfig } from "./build-helper.ts";
|
|
9
|
-
import { createManagedProject } from "./control-plane.ts";
|
|
9
|
+
import { createManagedProject, syncProjectTags } from "./control-plane.ts";
|
|
10
10
|
import { uploadDeployment } from "./deploy-upload.ts";
|
|
11
11
|
import { JackError, JackErrorCode } from "./errors.ts";
|
|
12
12
|
import type { OperationReporter } from "./project-operations.ts";
|
|
13
|
+
import { getProjectTags } from "./tags.ts";
|
|
13
14
|
import { Events, track } from "./telemetry.ts";
|
|
14
15
|
import { packageForDeploy } from "./zip-packager.ts";
|
|
15
16
|
|
|
@@ -138,6 +139,15 @@ export async function deployCodeToManagedProject(
|
|
|
138
139
|
duration_ms: Date.now() - startTime,
|
|
139
140
|
});
|
|
140
141
|
|
|
142
|
+
// Fire-and-forget tag sync (non-blocking)
|
|
143
|
+
getProjectTags(projectPath)
|
|
144
|
+
.then((tags) => {
|
|
145
|
+
if (tags.length > 0) {
|
|
146
|
+
void syncProjectTags(projectId, tags);
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
.catch(() => {});
|
|
150
|
+
|
|
141
151
|
return {
|
|
142
152
|
deploymentId: result.id,
|
|
143
153
|
status: result.status,
|
package/src/lib/managed-down.ts
CHANGED
|
@@ -8,25 +8,22 @@ import { join } from "node:path";
|
|
|
8
8
|
import { deleteManagedProject, exportManagedDatabase } from "./control-plane.ts";
|
|
9
9
|
import { promptSelect } from "./hooks.ts";
|
|
10
10
|
import { error, info, item, output, success, warn } from "./output.ts";
|
|
11
|
-
import type { Project } from "./registry.ts";
|
|
12
|
-
import { updateProject } from "./registry.ts";
|
|
13
11
|
|
|
14
12
|
export interface ManagedDownFlags {
|
|
15
13
|
force?: boolean;
|
|
16
14
|
}
|
|
17
15
|
|
|
16
|
+
export interface ManagedProjectInfo {
|
|
17
|
+
projectId: string;
|
|
18
|
+
runjackUrl: string | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
18
21
|
export async function managedDown(
|
|
19
|
-
project:
|
|
22
|
+
project: ManagedProjectInfo,
|
|
20
23
|
projectName: string,
|
|
21
24
|
flags: ManagedDownFlags = {},
|
|
22
25
|
): Promise<boolean> {
|
|
23
|
-
const
|
|
24
|
-
if (!remote?.project_id) {
|
|
25
|
-
throw new Error("Project is not linked to jack cloud");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const runjackUrl = remote.runjack_url;
|
|
29
|
-
const projectId = remote.project_id;
|
|
26
|
+
const { projectId, runjackUrl } = project;
|
|
30
27
|
|
|
31
28
|
// Force mode - quick deletion without prompts
|
|
32
29
|
if (flags.force) {
|
|
@@ -39,11 +36,6 @@ export async function managedDown(
|
|
|
39
36
|
await deleteManagedProject(projectId);
|
|
40
37
|
output.stop();
|
|
41
38
|
|
|
42
|
-
await updateProject(projectName, {
|
|
43
|
-
workerUrl: null,
|
|
44
|
-
lastDeployed: null,
|
|
45
|
-
});
|
|
46
|
-
|
|
47
39
|
console.error("");
|
|
48
40
|
success(`'${projectName}' undeployed`);
|
|
49
41
|
info("Database and backups were deleted");
|
|
@@ -142,11 +134,6 @@ export async function managedDown(
|
|
|
142
134
|
}
|
|
143
135
|
}
|
|
144
136
|
|
|
145
|
-
await updateProject(projectName, {
|
|
146
|
-
workerUrl: null,
|
|
147
|
-
lastDeployed: null,
|
|
148
|
-
});
|
|
149
|
-
|
|
150
137
|
console.error("");
|
|
151
138
|
success(`Project '${projectName}' undeployed`);
|
|
152
139
|
console.error("");
|