deepspace 0.5.7 → 0.6.1
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/CHANGELOG.md +16 -0
- package/dist/cli.js +286 -85
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +26 -3
- package/dist/index.js +124 -13
- package/dist/index.js.map +1 -1
- package/dist/testing.d.ts +115 -0
- package/features/cron/src/CronLogPage.tsx +3 -3
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// src/cli/cli.ts
|
|
4
4
|
import { defineCommand as defineCommand22, runMain } from "citty";
|
|
5
5
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
6
|
-
import { readFileSync as readFileSync17, existsSync as
|
|
7
|
-
import { dirname as dirname5, join as
|
|
6
|
+
import { readFileSync as readFileSync17, existsSync as existsSync15 } from "fs";
|
|
7
|
+
import { dirname as dirname5, join as join16 } from "path";
|
|
8
8
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
9
9
|
|
|
10
10
|
// src/cli/lib/cli-errors.ts
|
|
@@ -13,7 +13,9 @@ var API_ERROR_HINTS = {
|
|
|
13
13
|
app_not_found: "App not found. Check the app id \u2014 the DEEPSPACE_APP_ID value in wrangler.toml, usually `app_\u2026` (a legacy app's id is its name). List your apps with `deepspace apps`. (`domain` commands take the deployed app *name* instead.)",
|
|
14
14
|
not_app_owner_or_collaborator: "You must be the app owner or a collaborator to do this.",
|
|
15
15
|
test_account_cannot_be_collaborator: "Test accounts cannot be added as collaborators. Use a real DeepSpace account.",
|
|
16
|
-
user_not_found: "No DeepSpace user with that email. They need to log in to DeepSpace at least once (`deepspace login`, or sign in to any app) before they can be referenced by email."
|
|
16
|
+
user_not_found: "No DeepSpace user with that email. They need to log in to DeepSpace at least once (`deepspace login`, or sign in to any app) before they can be referenced by email.",
|
|
17
|
+
insufficient_credits: "Out of credits. Inviting a new collaborator by email sends them a transactional email billed to your account \u2014 top up your credits and try again.",
|
|
18
|
+
invite_email_failed: "The invite email could not be sent. You were not charged \u2014 please try again in a moment."
|
|
17
19
|
};
|
|
18
20
|
function formatCliError(err) {
|
|
19
21
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -555,18 +557,27 @@ function mintAppId(now = Date.now()) {
|
|
|
555
557
|
}
|
|
556
558
|
return `app_${ts}${rs}`.slice(0, 30);
|
|
557
559
|
}
|
|
558
|
-
async function
|
|
560
|
+
async function resolveExistingAppId(deployUrl, token, appName) {
|
|
561
|
+
const headers = { Authorization: `Bearer ${token}` };
|
|
559
562
|
try {
|
|
560
|
-
const res = await fetch(`${deployUrl}/api/apps`, {
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
563
|
+
const res = await fetch(`${deployUrl}/api/apps`, { headers });
|
|
564
|
+
if (res.ok) {
|
|
565
|
+
const body = await res.json();
|
|
566
|
+
const match = body.apps?.find((a) => a.name === appName);
|
|
567
|
+
if (match?.appId) return { kind: "adopted", appId: match.appId, owned: true };
|
|
568
|
+
}
|
|
569
|
+
} catch {
|
|
570
|
+
}
|
|
571
|
+
try {
|
|
572
|
+
const res = await fetch(
|
|
573
|
+
`${deployUrl}/api/apps/${encodeURIComponent(appName)}/analytics?period=1h`,
|
|
574
|
+
{ headers }
|
|
575
|
+
);
|
|
576
|
+
if (res.ok) return { kind: "adopted", appId: appName, owned: false };
|
|
577
|
+
if (res.status === 403) return { kind: "taken" };
|
|
567
578
|
} catch {
|
|
568
|
-
return null;
|
|
569
579
|
}
|
|
580
|
+
return { kind: "none" };
|
|
570
581
|
}
|
|
571
582
|
function readAppId(cwd = process.cwd(), wranglerEnv) {
|
|
572
583
|
const wranglerPath = join(resolve(cwd), "wrangler.toml");
|
|
@@ -1331,7 +1342,7 @@ var logout_default = defineCommand2({
|
|
|
1331
1342
|
|
|
1332
1343
|
// src/cli/commands/dev.ts
|
|
1333
1344
|
import { defineCommand as defineCommand3 } from "citty";
|
|
1334
|
-
import { resolve as resolve4, basename as basename2, join as
|
|
1345
|
+
import { resolve as resolve4, basename as basename2, join as join10 } from "path";
|
|
1335
1346
|
import spawn from "cross-spawn";
|
|
1336
1347
|
|
|
1337
1348
|
// src/cli/auth.ts
|
|
@@ -1777,6 +1788,112 @@ function removeMacosJunk(dir) {
|
|
|
1777
1788
|
return removed;
|
|
1778
1789
|
}
|
|
1779
1790
|
|
|
1791
|
+
// src/cli/lib/schema-lint.ts
|
|
1792
|
+
import { existsSync as existsSync9 } from "fs";
|
|
1793
|
+
import { join as join9 } from "path";
|
|
1794
|
+
import { pathToFileURL } from "url";
|
|
1795
|
+
|
|
1796
|
+
// src/server/schemas/registry.ts
|
|
1797
|
+
var USERS_COLUMNS = [
|
|
1798
|
+
{ name: "email", storage: "text", interpretation: "plain" },
|
|
1799
|
+
{ name: "name", storage: "text", interpretation: "plain" },
|
|
1800
|
+
{ name: "imageUrl", storage: "text", interpretation: "plain" },
|
|
1801
|
+
{ name: "role", storage: "text", interpretation: "plain" },
|
|
1802
|
+
{ name: "createdAt", storage: "text", interpretation: { kind: "datetime" } },
|
|
1803
|
+
{ name: "lastSeenAt", storage: "text", interpretation: { kind: "datetime" } }
|
|
1804
|
+
];
|
|
1805
|
+
var BASE_USERS_SCHEMA = {
|
|
1806
|
+
name: "users",
|
|
1807
|
+
columns: [...USERS_COLUMNS],
|
|
1808
|
+
permissions: {
|
|
1809
|
+
"*": { read: "own", create: false, update: false, delete: false },
|
|
1810
|
+
admin: { read: true, create: false, update: true, delete: true }
|
|
1811
|
+
}
|
|
1812
|
+
};
|
|
1813
|
+
function lintSchema(schema) {
|
|
1814
|
+
const warnings = [];
|
|
1815
|
+
const VISIBILITY_AWARE = ["published", "shared"];
|
|
1816
|
+
if (schema.visibilityField) {
|
|
1817
|
+
const offendingRoles = Object.entries(schema.permissions).filter(([, perms]) => perms.read === true).map(([role]) => role);
|
|
1818
|
+
if (offendingRoles.length > 0) {
|
|
1819
|
+
const usesAware = Object.values(schema.permissions).some(
|
|
1820
|
+
(p10) => typeof p10.read === "string" && VISIBILITY_AWARE.includes(p10.read)
|
|
1821
|
+
);
|
|
1822
|
+
if (!usesAware) {
|
|
1823
|
+
warnings.push(
|
|
1824
|
+
`[${schema.name}] visibilityField is declared but no role uses read: 'published' or 'shared'. Roles with read: true (${offendingRoles.join(", ")}) will see every row regardless of visibility. Change those to read: 'published' (owner OR public) or 'shared' (owner OR collaborator OR public) to actually enforce the filter, or remove visibilityField if you don't intend to gate reads by it.`
|
|
1825
|
+
);
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
if (schema.ownerField) {
|
|
1830
|
+
const col = schema.columns.find((c) => c.name === schema.ownerField);
|
|
1831
|
+
if (col && !col.userBound) {
|
|
1832
|
+
warnings.push(
|
|
1833
|
+
`[${schema.name}] ownerField is '${schema.ownerField}' but that column is not marked userBound: true. A client can create a row with someone else's id in this field, bypassing 'own' permission checks. Add userBound: true (and ideally immutable: true) to the column.`
|
|
1834
|
+
);
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
for (const col of schema.columns) {
|
|
1838
|
+
if (col.userBound && col.storage !== "text") {
|
|
1839
|
+
warnings.push(
|
|
1840
|
+
`[${schema.name}] column '${col.name}' is userBound but storage is '${col.storage}'. userBound stamps the user id (a string); use storage: 'text'.`
|
|
1841
|
+
);
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
return warnings;
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
// src/cli/lib/schema-lint.ts
|
|
1848
|
+
async function lintProjectSchemas(appDir) {
|
|
1849
|
+
const schemasPath = join9(appDir, "src", "schemas.ts");
|
|
1850
|
+
if (!existsSync9(schemasPath)) return null;
|
|
1851
|
+
let schemas;
|
|
1852
|
+
try {
|
|
1853
|
+
const esbuild = await import("esbuild");
|
|
1854
|
+
const outFile = join9(appDir, ".wrangler", "deploy", "schemas.lint.bundle.mjs");
|
|
1855
|
+
await esbuild.build({
|
|
1856
|
+
entryPoints: [schemasPath],
|
|
1857
|
+
bundle: true,
|
|
1858
|
+
format: "esm",
|
|
1859
|
+
platform: "node",
|
|
1860
|
+
target: "node18",
|
|
1861
|
+
outfile: outFile,
|
|
1862
|
+
logLevel: "silent",
|
|
1863
|
+
write: true
|
|
1864
|
+
});
|
|
1865
|
+
const mod = await import(`${pathToFileURL(outFile).href}?t=${Date.now()}`);
|
|
1866
|
+
schemas = mod.schemas;
|
|
1867
|
+
} catch {
|
|
1868
|
+
console.warn(
|
|
1869
|
+
"Schema lint skipped: could not bundle src/schemas.ts (the worker still lints at runtime)."
|
|
1870
|
+
);
|
|
1871
|
+
return null;
|
|
1872
|
+
}
|
|
1873
|
+
if (!Array.isArray(schemas)) return null;
|
|
1874
|
+
const findings = [];
|
|
1875
|
+
for (const schema of schemas) {
|
|
1876
|
+
if (!schema || typeof schema !== "object") continue;
|
|
1877
|
+
try {
|
|
1878
|
+
findings.push(...lintSchema(schema));
|
|
1879
|
+
} catch {
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
return findings;
|
|
1883
|
+
}
|
|
1884
|
+
var MAX_PRINTED_FINDINGS = 5;
|
|
1885
|
+
function formatSchemaLintFindings(findings) {
|
|
1886
|
+
if (findings.length === 0) return [];
|
|
1887
|
+
const n = findings.length;
|
|
1888
|
+
const shown = findings.slice(0, MAX_PRINTED_FINDINGS);
|
|
1889
|
+
const hidden = n - shown.length;
|
|
1890
|
+
return [
|
|
1891
|
+
`Schema lint: ${n} warning${n === 1 ? "" : "s"} in src/schemas.ts \u2014 ${n === 1 ? "this ships" : "these ship"} as permission/privacy bugs:`,
|
|
1892
|
+
...shown.map((f) => ` \u2022 ${f}`),
|
|
1893
|
+
...hidden > 0 ? [` \u2026and ${hidden} more \u2014 fix the above and re-run to see the rest.`] : []
|
|
1894
|
+
];
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1780
1897
|
// src/cli/lib/port.ts
|
|
1781
1898
|
import { createServer } from "net";
|
|
1782
1899
|
var DEFAULT_PORT = 5173;
|
|
@@ -1863,7 +1980,7 @@ var dev_default = defineCommand3({
|
|
|
1863
1980
|
);
|
|
1864
1981
|
} else {
|
|
1865
1982
|
console.warn(
|
|
1866
|
-
`Warning: could not update ${
|
|
1983
|
+
`Warning: could not update ${join10(worktree.mainRepoRoot, ".claude", "launch.json")} (malformed or unwritable) \u2014 the Claude preview tool may serve the main repo's code instead of this worktree.`
|
|
1867
1984
|
);
|
|
1868
1985
|
}
|
|
1869
1986
|
if (!explicitPort && process.env.DEEPSPACE_PORT) {
|
|
@@ -1876,6 +1993,10 @@ var dev_default = defineCommand3({
|
|
|
1876
1993
|
updatePort: explicitPort
|
|
1877
1994
|
});
|
|
1878
1995
|
ensureInstallReady(appDir);
|
|
1996
|
+
const lintFindings = await lintProjectSchemas(appDir);
|
|
1997
|
+
if (lintFindings && lintFindings.length > 0) {
|
|
1998
|
+
for (const line of formatSchemaLintFindings(lintFindings)) console.warn(line);
|
|
1999
|
+
}
|
|
1879
2000
|
let token;
|
|
1880
2001
|
try {
|
|
1881
2002
|
token = await ensureToken();
|
|
@@ -2463,15 +2584,15 @@ var screenshot_default = defineCommand6({
|
|
|
2463
2584
|
// src/cli/commands/test-accounts.ts
|
|
2464
2585
|
import { defineCommand as defineCommand7 } from "citty";
|
|
2465
2586
|
import * as p3 from "@clack/prompts";
|
|
2466
|
-
import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, existsSync as
|
|
2467
|
-
import { join as
|
|
2587
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
|
|
2588
|
+
import { join as join11 } from "path";
|
|
2468
2589
|
import { homedir as homedir4 } from "os";
|
|
2469
2590
|
var SESSION_COOKIE2 = "__Secure-better-auth.session_token";
|
|
2470
2591
|
var AUTH_URL4 = process.env.DEEPSPACE_AUTH_URL ?? PLATFORM_URLS.auth;
|
|
2471
|
-
var DIR3 =
|
|
2472
|
-
var ACCOUNTS_PATH =
|
|
2592
|
+
var DIR3 = join11(homedir4(), ".deepspace");
|
|
2593
|
+
var ACCOUNTS_PATH = join11(DIR3, "test-accounts.json");
|
|
2473
2594
|
function loadAccounts() {
|
|
2474
|
-
if (!
|
|
2595
|
+
if (!existsSync10(ACCOUNTS_PATH)) return [];
|
|
2475
2596
|
try {
|
|
2476
2597
|
return JSON.parse(readFileSync10(ACCOUNTS_PATH, "utf-8"));
|
|
2477
2598
|
} catch {
|
|
@@ -2753,9 +2874,9 @@ var test_accounts_default = defineCommand7({
|
|
|
2753
2874
|
import { defineCommand as defineCommand8 } from "citty";
|
|
2754
2875
|
import { execSync as execSync2 } from "child_process";
|
|
2755
2876
|
import { randomUUID } from "crypto";
|
|
2756
|
-
import { readFileSync as readFileSync11, readdirSync as readdirSync4, existsSync as
|
|
2757
|
-
import { join as
|
|
2758
|
-
import { pathToFileURL } from "url";
|
|
2877
|
+
import { readFileSync as readFileSync11, readdirSync as readdirSync4, existsSync as existsSync11 } from "fs";
|
|
2878
|
+
import { join as join12, resolve as resolve7, dirname as dirname2 } from "path";
|
|
2879
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
2759
2880
|
import * as p4 from "@clack/prompts";
|
|
2760
2881
|
var DEPLOY_URL3 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
2761
2882
|
var API_URL2 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
@@ -2893,6 +3014,11 @@ var deploy_default = defineCommand8({
|
|
|
2893
3014
|
type: "boolean",
|
|
2894
3015
|
description: "Deploy even though hand-edited .dev.vars secrets are absent from the store (they will NOT be deployed, and any a previous deploy set are dropped).",
|
|
2895
3016
|
default: false
|
|
3017
|
+
},
|
|
3018
|
+
adopt: {
|
|
3019
|
+
type: "boolean",
|
|
3020
|
+
description: "Confirm adopting an existing app you can deploy but do not own (collaborator/admin on-behalf) when wrangler.toml has no DEEPSPACE_APP_ID. Without this flag an interactive prompt asks.",
|
|
3021
|
+
default: false
|
|
2896
3022
|
}
|
|
2897
3023
|
},
|
|
2898
3024
|
async run({ args }) {
|
|
@@ -2932,16 +3058,37 @@ var deploy_default = defineCommand8({
|
|
|
2932
3058
|
process.exit(1);
|
|
2933
3059
|
}
|
|
2934
3060
|
if (!appId) {
|
|
2935
|
-
const
|
|
2936
|
-
|
|
3061
|
+
const existing = await resolveExistingAppId(DEPLOY_URL3, token, appName);
|
|
3062
|
+
if (existing.kind === "taken") {
|
|
3063
|
+
p4.cancel(
|
|
3064
|
+
`"${appName}" is taken by an app you don't have deploy access to. Ask the owner to add you as a collaborator (\`deepspace collaborators add\`), or pick a different \`name\` in wrangler.toml.`
|
|
3065
|
+
);
|
|
3066
|
+
process.exit(1);
|
|
3067
|
+
}
|
|
3068
|
+
if (existing.kind === "adopted" && !existing.owned && !args.adopt) {
|
|
3069
|
+
if (!process.stdin.isTTY) {
|
|
3070
|
+
p4.cancel(
|
|
3071
|
+
`"${appName}" is an existing app owned by another account that you can deploy on-behalf (collaborator/admin). Confirmation needs an interactive terminal \u2014 re-run with --adopt to deploy it, or pick a different \`name\` in wrangler.toml if you meant a new app.`
|
|
3072
|
+
);
|
|
3073
|
+
process.exit(1);
|
|
3074
|
+
}
|
|
3075
|
+
const yes = await p4.confirm({
|
|
3076
|
+
message: `"${appName}" is an existing app owned by another account that you can deploy on-behalf (you are a collaborator or admin). Deploy it? (Meant a new app? Pick a different \`name\` in wrangler.toml.)`
|
|
3077
|
+
});
|
|
3078
|
+
if (p4.isCancel(yes) || !yes) {
|
|
3079
|
+
p4.cancel("Deploy cancelled.");
|
|
3080
|
+
process.exit(1);
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
3083
|
+
appId = existing.kind === "adopted" ? existing.appId : mintAppId();
|
|
2937
3084
|
writeAppId(appDir, appId, { wranglerEnv: envName });
|
|
2938
3085
|
p4.log.info(
|
|
2939
|
-
adopted ? `Adopted existing app id ${appId} for ${appName} \u2014 commit wrangler.toml.` : `Minted app id ${appId} \u2014 commit wrangler.toml.`
|
|
3086
|
+
existing.kind === "adopted" ? `Adopted existing app id ${appId} for ${appName} \u2014 commit wrangler.toml.` : `Minted app id ${appId} \u2014 commit wrangler.toml.`
|
|
2940
3087
|
);
|
|
2941
3088
|
}
|
|
2942
3089
|
p4.log.info(`Id: ${appId}`);
|
|
2943
3090
|
const readCustomDevVarsAt = (path) => {
|
|
2944
|
-
if (!
|
|
3091
|
+
if (!existsSync11(path)) return {};
|
|
2945
3092
|
const section = extractCustomDevVars(stripGeneratedSecretsCache(readFileSync11(path, "utf-8")));
|
|
2946
3093
|
if (!section) return {};
|
|
2947
3094
|
try {
|
|
@@ -2980,6 +3127,10 @@ var deploy_default = defineCommand8({
|
|
|
2980
3127
|
}
|
|
2981
3128
|
const junk = removeMacosJunk(appDir);
|
|
2982
3129
|
if (junk > 0) p4.log.info(`Removed ${junk} macOS metadata file(s) (._*, .DS_Store)`);
|
|
3130
|
+
const lintFindings = await lintProjectSchemas(appDir);
|
|
3131
|
+
if (lintFindings && lintFindings.length > 0) {
|
|
3132
|
+
p4.log.warn(formatSchemaLintFindings(lintFindings).join("\n"));
|
|
3133
|
+
}
|
|
2983
3134
|
const s = createSpinner();
|
|
2984
3135
|
s.start("Building...");
|
|
2985
3136
|
let preparedWranglerConfig;
|
|
@@ -3002,14 +3153,14 @@ var deploy_default = defineCommand8({
|
|
|
3002
3153
|
preparedWranglerConfig?.cleanup();
|
|
3003
3154
|
}
|
|
3004
3155
|
s.stop("Built");
|
|
3005
|
-
const deployConfigPath =
|
|
3006
|
-
if (!
|
|
3156
|
+
const deployConfigPath = join12(appDir, ".wrangler", "deploy", "config.json");
|
|
3157
|
+
if (!existsSync11(deployConfigPath)) {
|
|
3007
3158
|
p4.cancel("Build output config not found at .wrangler/deploy/config.json");
|
|
3008
3159
|
process.exit(1);
|
|
3009
3160
|
}
|
|
3010
3161
|
const deployConfig = JSON.parse(readFileSync11(deployConfigPath, "utf-8"));
|
|
3011
3162
|
const outputWranglerPath = resolve7(dirname2(deployConfigPath), deployConfig.configPath);
|
|
3012
|
-
if (!
|
|
3163
|
+
if (!existsSync11(outputWranglerPath)) {
|
|
3013
3164
|
p4.cancel(`Output wrangler.json not found at ${outputWranglerPath}`);
|
|
3014
3165
|
process.exit(1);
|
|
3015
3166
|
}
|
|
@@ -3022,13 +3173,13 @@ var deploy_default = defineCommand8({
|
|
|
3022
3173
|
process.exit(1);
|
|
3023
3174
|
}
|
|
3024
3175
|
const workerDir = dirname2(outputWranglerPath);
|
|
3025
|
-
const workerBundlePath =
|
|
3176
|
+
const workerBundlePath = join12(workerDir, outputConfig.main);
|
|
3026
3177
|
const clientDir = outputConfig.assets?.directory ? resolve7(workerDir, outputConfig.assets.directory) : null;
|
|
3027
|
-
if (!
|
|
3178
|
+
if (!existsSync11(workerBundlePath)) {
|
|
3028
3179
|
p4.cancel(`Worker bundle not found at ${workerBundlePath}`);
|
|
3029
3180
|
process.exit(1);
|
|
3030
3181
|
}
|
|
3031
|
-
if (!clientDir || !
|
|
3182
|
+
if (!clientDir || !existsSync11(clientDir)) {
|
|
3032
3183
|
p4.cancel(`Client assets not found at ${clientDir}`);
|
|
3033
3184
|
process.exit(1);
|
|
3034
3185
|
}
|
|
@@ -3263,13 +3414,13 @@ then redeploy. (Pass --allow-missing-secrets to deploy without them.)`
|
|
|
3263
3414
|
}
|
|
3264
3415
|
});
|
|
3265
3416
|
async function syncSubscriptionPlans(appDir, appName, token) {
|
|
3266
|
-
const subsPath =
|
|
3267
|
-
if (!
|
|
3417
|
+
const subsPath = join12(appDir, "src", "subscriptions.ts");
|
|
3418
|
+
if (!existsSync11(subsPath)) return;
|
|
3268
3419
|
let plans;
|
|
3269
3420
|
try {
|
|
3270
3421
|
const esbuild = await import("esbuild");
|
|
3271
|
-
const outDir =
|
|
3272
|
-
const outFile =
|
|
3422
|
+
const outDir = join12(appDir, ".wrangler", "deploy");
|
|
3423
|
+
const outFile = join12(outDir, "subscriptions.bundle.mjs");
|
|
3273
3424
|
await esbuild.build({
|
|
3274
3425
|
entryPoints: [subsPath],
|
|
3275
3426
|
bundle: true,
|
|
@@ -3280,7 +3431,7 @@ async function syncSubscriptionPlans(appDir, appName, token) {
|
|
|
3280
3431
|
logLevel: "silent",
|
|
3281
3432
|
write: true
|
|
3282
3433
|
});
|
|
3283
|
-
const mod = await import(`${
|
|
3434
|
+
const mod = await import(`${pathToFileURL2(outFile).href}?t=${Date.now()}`);
|
|
3284
3435
|
plans = mod.subscriptionPlans;
|
|
3285
3436
|
} catch (err) {
|
|
3286
3437
|
p4.log.warn(`Could not load src/subscriptions.ts: ${errMessage(err)}`);
|
|
@@ -3411,13 +3562,13 @@ current billing period? (Skip to leave them grandfathered.)`
|
|
|
3411
3562
|
}
|
|
3412
3563
|
}
|
|
3413
3564
|
async function syncOneTimeProducts(appDir, appName, token) {
|
|
3414
|
-
const productsPath =
|
|
3415
|
-
if (!
|
|
3565
|
+
const productsPath = join12(appDir, "src", "products.ts");
|
|
3566
|
+
if (!existsSync11(productsPath)) return;
|
|
3416
3567
|
let products;
|
|
3417
3568
|
try {
|
|
3418
3569
|
const esbuild = await import("esbuild");
|
|
3419
|
-
const outDir =
|
|
3420
|
-
const outFile =
|
|
3570
|
+
const outDir = join12(appDir, ".wrangler", "deploy");
|
|
3571
|
+
const outFile = join12(outDir, "products.bundle.mjs");
|
|
3421
3572
|
await esbuild.build({
|
|
3422
3573
|
entryPoints: [productsPath],
|
|
3423
3574
|
bundle: true,
|
|
@@ -3428,7 +3579,7 @@ async function syncOneTimeProducts(appDir, appName, token) {
|
|
|
3428
3579
|
logLevel: "silent",
|
|
3429
3580
|
write: true
|
|
3430
3581
|
});
|
|
3431
|
-
const mod = await import(`${
|
|
3582
|
+
const mod = await import(`${pathToFileURL2(outFile).href}?t=${Date.now()}`);
|
|
3432
3583
|
products = mod.oneTimeProducts;
|
|
3433
3584
|
} catch (err) {
|
|
3434
3585
|
p4.log.warn(`Could not load src/products.ts: ${errMessage(err)}`);
|
|
@@ -3492,7 +3643,7 @@ function collectAssets(dir) {
|
|
|
3492
3643
|
function walk(d, prefix) {
|
|
3493
3644
|
for (const entry of readdirSync4(d, { withFileTypes: true })) {
|
|
3494
3645
|
if (entry.name === ".assetsignore") continue;
|
|
3495
|
-
const full =
|
|
3646
|
+
const full = join12(d, entry.name);
|
|
3496
3647
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
3497
3648
|
if (entry.isDirectory()) {
|
|
3498
3649
|
walk(full, rel);
|
|
@@ -3673,26 +3824,26 @@ var apps_default = defineCommand11({
|
|
|
3673
3824
|
|
|
3674
3825
|
// src/cli/commands/add.ts
|
|
3675
3826
|
import { defineCommand as defineCommand12 } from "citty";
|
|
3676
|
-
import { existsSync as
|
|
3677
|
-
import { resolve as resolve8, join as
|
|
3827
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12 } from "fs";
|
|
3828
|
+
import { resolve as resolve8, join as join13, dirname as dirname3 } from "path";
|
|
3678
3829
|
import { fileURLToPath } from "url";
|
|
3679
3830
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
3680
3831
|
function findScript(from) {
|
|
3681
|
-
const inNodeModules =
|
|
3682
|
-
if (
|
|
3683
|
-
const inDotDeepspace =
|
|
3684
|
-
if (
|
|
3832
|
+
const inNodeModules = join13(from, "node_modules", "deepspace", "scripts", "add-feature.cjs");
|
|
3833
|
+
if (existsSync12(inNodeModules)) return inNodeModules;
|
|
3834
|
+
const inDotDeepspace = join13(from, ".deepspace", "scripts", "add-feature.cjs");
|
|
3835
|
+
if (existsSync12(inDotDeepspace)) return inDotDeepspace;
|
|
3685
3836
|
return null;
|
|
3686
3837
|
}
|
|
3687
3838
|
function runningScript() {
|
|
3688
3839
|
const here = dirname3(fileURLToPath(import.meta.url));
|
|
3689
|
-
const script =
|
|
3690
|
-
return
|
|
3840
|
+
const script = join13(here, "..", "scripts", "add-feature.cjs");
|
|
3841
|
+
return existsSync12(script) ? script : null;
|
|
3691
3842
|
}
|
|
3692
3843
|
function isDeepSpaceApp(dir) {
|
|
3693
|
-
if (
|
|
3694
|
-
const pkgPath =
|
|
3695
|
-
if (!
|
|
3844
|
+
if (existsSync12(join13(dir, "wrangler.toml"))) return true;
|
|
3845
|
+
const pkgPath = join13(dir, "package.json");
|
|
3846
|
+
if (!existsSync12(pkgPath)) return false;
|
|
3696
3847
|
try {
|
|
3697
3848
|
const pkg2 = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
3698
3849
|
return Boolean(pkg2.dependencies?.deepspace || pkg2.devDependencies?.deepspace);
|
|
@@ -3753,7 +3904,7 @@ var add_default = defineCommand12({
|
|
|
3753
3904
|
const passArgs2 = args.list ? ["--list"] : args.info ? ["--info", String(args.info)] : ["--help"];
|
|
3754
3905
|
runInstaller(script2, passArgs2, process.cwd());
|
|
3755
3906
|
}
|
|
3756
|
-
if (!
|
|
3907
|
+
if (!existsSync12(appDir)) {
|
|
3757
3908
|
console.error(`Target directory not found: ${appDir}`);
|
|
3758
3909
|
console.error("Create a new app first: deepspace create <name>");
|
|
3759
3910
|
process.exit(1);
|
|
@@ -3769,8 +3920,8 @@ var add_default = defineCommand12({
|
|
|
3769
3920
|
if (!script) {
|
|
3770
3921
|
console.error("Could not find the deepspace feature installer.");
|
|
3771
3922
|
console.error("Looked in:");
|
|
3772
|
-
console.error(` ${
|
|
3773
|
-
console.error(` ${
|
|
3923
|
+
console.error(` ${join13(appDir, "node_modules", "deepspace", "scripts", "add-feature.cjs")}`);
|
|
3924
|
+
console.error(` ${join13(appDir, ".deepspace", "scripts", "add-feature.cjs")}`);
|
|
3774
3925
|
console.error("Are you in a DeepSpace app directory? Try `npm install` first.");
|
|
3775
3926
|
process.exit(1);
|
|
3776
3927
|
}
|
|
@@ -4131,30 +4282,35 @@ var list3 = defineCommand14({
|
|
|
4131
4282
|
async run({ args }) {
|
|
4132
4283
|
const token = await ensureToken();
|
|
4133
4284
|
const app = await resolveAppTarget(DEPLOY_URL6, token, args.app);
|
|
4134
|
-
const { collaborators } = await api2(
|
|
4135
|
-
token,
|
|
4136
|
-
`/api/app-collaborators/${encodeURIComponent(app)}`
|
|
4137
|
-
);
|
|
4285
|
+
const { collaborators, pending = [] } = await api2(token, `/api/app-collaborators/${encodeURIComponent(app)}`);
|
|
4138
4286
|
if (args.json) {
|
|
4139
|
-
process.stdout.write(JSON.stringify(collaborators, null, 2) + "\n");
|
|
4287
|
+
process.stdout.write(JSON.stringify({ collaborators, pending }, null, 2) + "\n");
|
|
4140
4288
|
return;
|
|
4141
4289
|
}
|
|
4142
|
-
if (!collaborators.length) {
|
|
4290
|
+
if (!collaborators.length && !pending.length) {
|
|
4143
4291
|
console.log(`No collaborators on ${app}. Add one with \`deepspace collaborators add <email>\`.`);
|
|
4144
4292
|
return;
|
|
4145
4293
|
}
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4294
|
+
if (collaborators.length) {
|
|
4295
|
+
console.log(`COLLABORATORS ON ${app}`);
|
|
4296
|
+
for (const c of collaborators) {
|
|
4297
|
+
console.log(` ${c.emailDisplay}`);
|
|
4298
|
+
}
|
|
4299
|
+
}
|
|
4300
|
+
if (pending.length) {
|
|
4301
|
+
console.log(`PENDING INVITES ON ${app}`);
|
|
4302
|
+
for (const p10 of pending) {
|
|
4303
|
+
console.log(` ${p10.email} (expires ${new Date(p10.expiresAt).toLocaleDateString()})`);
|
|
4304
|
+
}
|
|
4149
4305
|
}
|
|
4150
4306
|
}
|
|
4151
4307
|
});
|
|
4152
4308
|
var add = defineCommand14({
|
|
4153
|
-
meta: { name: "add", description: "Authorize
|
|
4309
|
+
meta: { name: "add", description: "Authorize someone to deploy your app" },
|
|
4154
4310
|
args: {
|
|
4155
4311
|
email: {
|
|
4156
4312
|
type: "positional",
|
|
4157
|
-
description: "Collaborator email
|
|
4313
|
+
description: "Collaborator email",
|
|
4158
4314
|
required: true
|
|
4159
4315
|
},
|
|
4160
4316
|
app: { type: "string", alias: "a", description: "App id or name (defaults to ./wrangler.toml)" },
|
|
@@ -4169,16 +4325,29 @@ var add = defineCommand14({
|
|
|
4169
4325
|
);
|
|
4170
4326
|
process.exit(1);
|
|
4171
4327
|
}
|
|
4172
|
-
const
|
|
4328
|
+
const res = await api2(
|
|
4173
4329
|
token,
|
|
4174
4330
|
`/api/app-collaborators/${encodeURIComponent(app)}`,
|
|
4175
4331
|
{ method: "POST", body: JSON.stringify({ email: args.email }) }
|
|
4176
4332
|
);
|
|
4177
4333
|
if (args.json) {
|
|
4178
|
-
process.stdout.write(JSON.stringify(
|
|
4334
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
4179
4335
|
return;
|
|
4180
4336
|
}
|
|
4181
|
-
|
|
4337
|
+
if (res.status === "invited" || res.status === "already_invited") {
|
|
4338
|
+
const expires = new Date(res.expiresAt).toLocaleDateString();
|
|
4339
|
+
if (res.status === "already_invited") {
|
|
4340
|
+
console.log(
|
|
4341
|
+
`\u2022 ${res.email} already has a pending invite (expires ${expires}). No new email was sent. Cancel it with \`deepspace collaborators cancel ${res.email}\` to reset.`
|
|
4342
|
+
);
|
|
4343
|
+
return;
|
|
4344
|
+
}
|
|
4345
|
+
console.log(
|
|
4346
|
+
`\u2713 Invite sent to ${res.email} (expires ${expires}). They become a collaborator when they sign in.`
|
|
4347
|
+
);
|
|
4348
|
+
return;
|
|
4349
|
+
}
|
|
4350
|
+
console.log(`\u2713 ${res.collaborator.emailDisplay} can now deploy ${app}`);
|
|
4182
4351
|
}
|
|
4183
4352
|
});
|
|
4184
4353
|
var remove = defineCommand14({
|
|
@@ -4213,9 +4382,41 @@ var remove = defineCommand14({
|
|
|
4213
4382
|
console.log(`\u2713 ${found.emailDisplay} can no longer deploy ${app}`);
|
|
4214
4383
|
}
|
|
4215
4384
|
});
|
|
4385
|
+
var cancel4 = defineCommand14({
|
|
4386
|
+
meta: { name: "cancel", description: "Cancel a pending (un-accepted) email invite" },
|
|
4387
|
+
args: {
|
|
4388
|
+
email: { type: "positional", description: "Invited email", required: true },
|
|
4389
|
+
app: { type: "string", alias: "a", description: "App id or name (defaults to ./wrangler.toml)" },
|
|
4390
|
+
json: { type: "boolean", description: "Emit JSON" }
|
|
4391
|
+
},
|
|
4392
|
+
async run({ args }) {
|
|
4393
|
+
const token = await ensureToken();
|
|
4394
|
+
const app = await resolveAppTarget(DEPLOY_URL6, token, args.app);
|
|
4395
|
+
const { pending = [] } = await api2(
|
|
4396
|
+
token,
|
|
4397
|
+
`/api/app-collaborators/${encodeURIComponent(app)}`
|
|
4398
|
+
);
|
|
4399
|
+
const target = args.email.trim().toLowerCase();
|
|
4400
|
+
const found = pending.find((p10) => p10.email.toLowerCase() === target);
|
|
4401
|
+
if (!found) {
|
|
4402
|
+
console.error(`${args.email} has no pending invite on ${app}`);
|
|
4403
|
+
process.exit(1);
|
|
4404
|
+
}
|
|
4405
|
+
await api2(
|
|
4406
|
+
token,
|
|
4407
|
+
`/api/app-collaborators/${encodeURIComponent(app)}/pending/${encodeURIComponent(found.email)}`,
|
|
4408
|
+
{ method: "DELETE" }
|
|
4409
|
+
);
|
|
4410
|
+
if (args.json) {
|
|
4411
|
+
process.stdout.write(JSON.stringify({ cancelled: found }, null, 2) + "\n");
|
|
4412
|
+
return;
|
|
4413
|
+
}
|
|
4414
|
+
console.log(`\u2713 Cancelled the pending invite to ${found.email} on ${app}`);
|
|
4415
|
+
}
|
|
4416
|
+
});
|
|
4216
4417
|
var collaborators_default = defineCommand14({
|
|
4217
4418
|
meta: { name: "collaborators", description: "Manage who can deploy your app" },
|
|
4218
|
-
subCommands: { list: list3, add, remove }
|
|
4419
|
+
subCommands: { list: list3, add, remove, cancel: cancel4 }
|
|
4219
4420
|
});
|
|
4220
4421
|
|
|
4221
4422
|
// src/cli/commands/transfer.ts
|
|
@@ -4304,7 +4505,7 @@ var accept = defineCommand15({
|
|
|
4304
4505
|
);
|
|
4305
4506
|
}
|
|
4306
4507
|
});
|
|
4307
|
-
var
|
|
4508
|
+
var cancel5 = defineCommand15({
|
|
4308
4509
|
meta: { name: "cancel", description: "Cancel/decline the pending offer (either party)" },
|
|
4309
4510
|
args: {
|
|
4310
4511
|
app: { type: "string", alias: "a", description: "App id or name (defaults to ./wrangler.toml)" }
|
|
@@ -4318,7 +4519,7 @@ var cancel4 = defineCommand15({
|
|
|
4318
4519
|
});
|
|
4319
4520
|
var transfer_default = defineCommand15({
|
|
4320
4521
|
meta: { name: "transfer", description: "Transfer app ownership to another user" },
|
|
4321
|
-
subCommands: { offer, status: status2, accept, cancel:
|
|
4522
|
+
subCommands: { offer, status: status2, accept, cancel: cancel5 }
|
|
4322
4523
|
});
|
|
4323
4524
|
|
|
4324
4525
|
// src/cli/commands/integrations.ts
|
|
@@ -4746,8 +4947,8 @@ var invoke_default = defineCommand17({
|
|
|
4746
4947
|
|
|
4747
4948
|
// src/cli/commands/library.ts
|
|
4748
4949
|
import { defineCommand as defineCommand18 } from "citty";
|
|
4749
|
-
import { existsSync as
|
|
4750
|
-
import { join as
|
|
4950
|
+
import { existsSync as existsSync13, readFileSync as readFileSync14 } from "fs";
|
|
4951
|
+
import { join as join14, resolve as resolve9 } from "path";
|
|
4751
4952
|
import { parse as parseToml4 } from "smol-toml";
|
|
4752
4953
|
import * as p8 from "@clack/prompts";
|
|
4753
4954
|
var DEFAULT_LIBRARY_APP = "deepdotspace-site";
|
|
@@ -4825,8 +5026,8 @@ var publish = defineCommand18({
|
|
|
4825
5026
|
let appName = typeof args.app === "string" && args.app.trim() ? args.app.trim() : void 0;
|
|
4826
5027
|
if (!appName) {
|
|
4827
5028
|
const appDir = resolve9(typeof args.dir === "string" && args.dir ? args.dir : ".");
|
|
4828
|
-
const wranglerPath =
|
|
4829
|
-
if (!
|
|
5029
|
+
const wranglerPath = join14(appDir, "wrangler.toml");
|
|
5030
|
+
if (!existsSync13(wranglerPath)) {
|
|
4830
5031
|
return die("No --app provided and no wrangler.toml found. Either pass --app <name> or run from your app's directory.");
|
|
4831
5032
|
}
|
|
4832
5033
|
const cfg = parseToml4(readFileSync14(wranglerPath, "utf-8"));
|
|
@@ -4963,8 +5164,8 @@ var library_default = defineCommand18({
|
|
|
4963
5164
|
// src/cli/commands/feedback.ts
|
|
4964
5165
|
import { defineCommand as defineCommand19 } from "citty";
|
|
4965
5166
|
import * as p9 from "@clack/prompts";
|
|
4966
|
-
import { existsSync as
|
|
4967
|
-
import { dirname as dirname4, join as
|
|
5167
|
+
import { existsSync as existsSync14, readFileSync as readFileSync15 } from "fs";
|
|
5168
|
+
import { dirname as dirname4, join as join15 } from "path";
|
|
4968
5169
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4969
5170
|
import { release } from "os";
|
|
4970
5171
|
var API_URL7 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
@@ -4987,11 +5188,11 @@ function readCliVersion() {
|
|
|
4987
5188
|
try {
|
|
4988
5189
|
const here = dirname4(fileURLToPath2(import.meta.url));
|
|
4989
5190
|
const candidates = [
|
|
4990
|
-
|
|
4991
|
-
|
|
5191
|
+
join15(here, "..", "package.json"),
|
|
5192
|
+
join15(here, "..", "..", "..", "package.json")
|
|
4992
5193
|
];
|
|
4993
5194
|
for (const path of candidates) {
|
|
4994
|
-
if (!
|
|
5195
|
+
if (!existsSync14(path)) continue;
|
|
4995
5196
|
const pkg2 = JSON.parse(readFileSync15(path, "utf-8"));
|
|
4996
5197
|
if (pkg2.name === "deepspace" && pkg2.version) return pkg2.version;
|
|
4997
5198
|
}
|
|
@@ -5609,8 +5810,8 @@ var init_default = defineCommand21({
|
|
|
5609
5810
|
|
|
5610
5811
|
// src/cli/cli.ts
|
|
5611
5812
|
var __dirname = dirname5(fileURLToPath3(import.meta.url));
|
|
5612
|
-
var pkg = JSON.parse(readFileSync17(
|
|
5613
|
-
if (process.platform === "linux" && !process.env.SSL_CERT_FILE &&
|
|
5813
|
+
var pkg = JSON.parse(readFileSync17(join16(__dirname, "..", "package.json"), "utf-8"));
|
|
5814
|
+
if (process.platform === "linux" && !process.env.SSL_CERT_FILE && existsSync15("/etc/ssl/certs/ca-certificates.crt")) {
|
|
5614
5815
|
process.env.SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
|
|
5615
5816
|
}
|
|
5616
5817
|
var create2 = defineCommand22({
|