@supatype/cli 0.1.0-alpha.10
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/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-test.log +221 -0
- package/.turbo/turbo-typecheck.log +4 -0
- package/assets/supatype-logo-wordmark.ascii.txt +6 -0
- package/bin/dev-entry.ts +2 -0
- package/bin/supatype.js +5 -0
- package/dist/app/framework.d.ts +44 -0
- package/dist/app/framework.d.ts.map +1 -0
- package/dist/app/framework.js +200 -0
- package/dist/app/framework.js.map +1 -0
- package/dist/app/proxy-dev-app.d.ts +13 -0
- package/dist/app/proxy-dev-app.d.ts.map +1 -0
- package/dist/app/proxy-dev-app.js +54 -0
- package/dist/app/proxy-dev-app.js.map +1 -0
- package/dist/app-config.d.ts +7 -0
- package/dist/app-config.d.ts.map +1 -0
- package/dist/app-config.js +113 -0
- package/dist/app-config.js.map +1 -0
- package/dist/assets/supatype-logo-wordmark.ascii.txt +6 -0
- package/dist/augmentation-generator.d.ts +2 -0
- package/dist/augmentation-generator.d.ts.map +1 -0
- package/dist/augmentation-generator.js +111 -0
- package/dist/augmentation-generator.js.map +1 -0
- package/dist/binary-cache.d.ts +98 -0
- package/dist/binary-cache.d.ts.map +1 -0
- package/dist/binary-cache.js +687 -0
- package/dist/binary-cache.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +61 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/admin.d.ts +4 -0
- package/dist/commands/admin.d.ts.map +1 -0
- package/dist/commands/admin.js +271 -0
- package/dist/commands/admin.js.map +1 -0
- package/dist/commands/app.d.ts +3 -0
- package/dist/commands/app.d.ts.map +1 -0
- package/dist/commands/app.js +82 -0
- package/dist/commands/app.js.map +1 -0
- package/dist/commands/cache.d.ts +6 -0
- package/dist/commands/cache.d.ts.map +1 -0
- package/dist/commands/cache.js +105 -0
- package/dist/commands/cache.js.map +1 -0
- package/dist/commands/cloud.d.ts +23 -0
- package/dist/commands/cloud.d.ts.map +1 -0
- package/dist/commands/cloud.js +254 -0
- package/dist/commands/cloud.js.map +1 -0
- package/dist/commands/db.d.ts +8 -0
- package/dist/commands/db.d.ts.map +1 -0
- package/dist/commands/db.js +116 -0
- package/dist/commands/db.js.map +1 -0
- package/dist/commands/deploy-types.d.ts +14 -0
- package/dist/commands/deploy-types.d.ts.map +1 -0
- package/dist/commands/deploy-types.js +38 -0
- package/dist/commands/deploy-types.js.map +1 -0
- package/dist/commands/deploy.d.ts +15 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +322 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/dev.d.ts +14 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +806 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/diff.d.ts +3 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +54 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/engine.d.ts +7 -0
- package/dist/commands/engine.d.ts.map +1 -0
- package/dist/commands/engine.js +27 -0
- package/dist/commands/engine.js.map +1 -0
- package/dist/commands/functions.d.ts +3 -0
- package/dist/commands/functions.d.ts.map +1 -0
- package/dist/commands/functions.js +749 -0
- package/dist/commands/functions.js.map +1 -0
- package/dist/commands/generate.d.ts +3 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +38 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +228 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/keys.d.ts +4 -0
- package/dist/commands/keys.d.ts.map +1 -0
- package/dist/commands/keys.js +57 -0
- package/dist/commands/keys.js.map +1 -0
- package/dist/commands/logs.d.ts +6 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +52 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/migrate-from-v1.d.ts +5 -0
- package/dist/commands/migrate-from-v1.d.ts.map +1 -0
- package/dist/commands/migrate-from-v1.js +125 -0
- package/dist/commands/migrate-from-v1.js.map +1 -0
- package/dist/commands/migrate.d.ts +3 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +75 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/pg.d.ts +8 -0
- package/dist/commands/pg.d.ts.map +1 -0
- package/dist/commands/pg.js +102 -0
- package/dist/commands/pg.js.map +1 -0
- package/dist/commands/plugins.d.ts +3 -0
- package/dist/commands/plugins.d.ts.map +1 -0
- package/dist/commands/plugins.js +431 -0
- package/dist/commands/plugins.js.map +1 -0
- package/dist/commands/pull.d.ts +3 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +12 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/push.d.ts +3 -0
- package/dist/commands/push.d.ts.map +1 -0
- package/dist/commands/push.js +179 -0
- package/dist/commands/push.js.map +1 -0
- package/dist/commands/seed.d.ts +5 -0
- package/dist/commands/seed.d.ts.map +1 -0
- package/dist/commands/seed.js +55 -0
- package/dist/commands/seed.js.map +1 -0
- package/dist/commands/self-host.d.ts +9 -0
- package/dist/commands/self-host.d.ts.map +1 -0
- package/dist/commands/self-host.js +310 -0
- package/dist/commands/self-host.js.map +1 -0
- package/dist/commands/self-update.d.ts +9 -0
- package/dist/commands/self-update.d.ts.map +1 -0
- package/dist/commands/self-update.js +33 -0
- package/dist/commands/self-update.js.map +1 -0
- package/dist/commands/status.d.ts +6 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +70 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/types.d.ts +3 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +62 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/commands/update.d.ts +7 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +118 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/components.d.ts +5 -0
- package/dist/components.d.ts.map +1 -0
- package/dist/components.js +3 -0
- package/dist/components.js.map +1 -0
- package/dist/config.d.ts +65 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +134 -0
- package/dist/config.js.map +1 -0
- package/dist/dev-compose.d.ts +19 -0
- package/dist/dev-compose.d.ts.map +1 -0
- package/dist/dev-compose.js +468 -0
- package/dist/dev-compose.js.map +1 -0
- package/dist/dev-log-bus.d.ts +30 -0
- package/dist/dev-log-bus.d.ts.map +1 -0
- package/dist/dev-log-bus.js +87 -0
- package/dist/dev-log-bus.js.map +1 -0
- package/dist/dev-log-filter.d.ts +10 -0
- package/dist/dev-log-filter.d.ts.map +1 -0
- package/dist/dev-log-filter.js +36 -0
- package/dist/dev-log-filter.js.map +1 -0
- package/dist/dev-logo.d.ts +12 -0
- package/dist/dev-logo.d.ts.map +1 -0
- package/dist/dev-logo.js +57 -0
- package/dist/dev-logo.js.map +1 -0
- package/dist/dev-session.d.ts +26 -0
- package/dist/dev-session.d.ts.map +1 -0
- package/dist/dev-session.js +106 -0
- package/dist/dev-session.js.map +1 -0
- package/dist/dev-shutdown.d.ts +9 -0
- package/dist/dev-shutdown.d.ts.map +1 -0
- package/dist/dev-shutdown.js +50 -0
- package/dist/dev-shutdown.js.map +1 -0
- package/dist/dev-task-colors.d.ts +14 -0
- package/dist/dev-task-colors.d.ts.map +1 -0
- package/dist/dev-task-colors.js +44 -0
- package/dist/dev-task-colors.js.map +1 -0
- package/dist/dev-tui.d.ts +24 -0
- package/dist/dev-tui.d.ts.map +1 -0
- package/dist/dev-tui.js +188 -0
- package/dist/dev-tui.js.map +1 -0
- package/dist/diff-output.d.ts +4 -0
- package/dist/diff-output.d.ts.map +1 -0
- package/dist/diff-output.js +12 -0
- package/dist/diff-output.js.map +1 -0
- package/dist/docker-postgres.d.ts +57 -0
- package/dist/docker-postgres.d.ts.map +1 -0
- package/dist/docker-postgres.js +208 -0
- package/dist/docker-postgres.js.map +1 -0
- package/dist/engine-client.d.ts +69 -0
- package/dist/engine-client.d.ts.map +1 -0
- package/dist/engine-client.js +157 -0
- package/dist/engine-client.js.map +1 -0
- package/dist/engine-push-output.d.ts +16 -0
- package/dist/engine-push-output.d.ts.map +1 -0
- package/dist/engine-push-output.js +61 -0
- package/dist/engine-push-output.js.map +1 -0
- package/dist/ensure-binary.d.ts +7 -0
- package/dist/ensure-binary.d.ts.map +1 -0
- package/dist/ensure-binary.js +17 -0
- package/dist/ensure-binary.js.map +1 -0
- package/dist/functions-router-gen.d.ts +14 -0
- package/dist/functions-router-gen.d.ts.map +1 -0
- package/dist/functions-router-gen.js +199 -0
- package/dist/functions-router-gen.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/jwt.d.ts +3 -0
- package/dist/jwt.d.ts.map +1 -0
- package/dist/jwt.js +13 -0
- package/dist/jwt.js.map +1 -0
- package/dist/kong-config.d.ts +25 -0
- package/dist/kong-config.d.ts.map +1 -0
- package/dist/kong-config.js +71 -0
- package/dist/kong-config.js.map +1 -0
- package/dist/local-gateway.d.ts +7 -0
- package/dist/local-gateway.d.ts.map +1 -0
- package/dist/local-gateway.js +9 -0
- package/dist/local-gateway.js.map +1 -0
- package/dist/local-storage.d.ts +8 -0
- package/dist/local-storage.d.ts.map +1 -0
- package/dist/local-storage.js +14 -0
- package/dist/local-storage.js.map +1 -0
- package/dist/pgbouncer-userlist.d.ts +5 -0
- package/dist/pgbouncer-userlist.d.ts.map +1 -0
- package/dist/pgbouncer-userlist.js +14 -0
- package/dist/pgbouncer-userlist.js.map +1 -0
- package/dist/postgres-ctl.d.ts +44 -0
- package/dist/postgres-ctl.d.ts.map +1 -0
- package/dist/postgres-ctl.js +137 -0
- package/dist/postgres-ctl.js.map +1 -0
- package/dist/process-manager.d.ts +49 -0
- package/dist/process-manager.d.ts.map +1 -0
- package/dist/process-manager.js +177 -0
- package/dist/process-manager.js.map +1 -0
- package/dist/project-config.d.ts +238 -0
- package/dist/project-config.d.ts.map +1 -0
- package/dist/project-config.js +159 -0
- package/dist/project-config.js.map +1 -0
- package/dist/pull-utils.d.ts +31 -0
- package/dist/pull-utils.d.ts.map +1 -0
- package/dist/pull-utils.js +77 -0
- package/dist/pull-utils.js.map +1 -0
- package/dist/release-pins.d.ts +7 -0
- package/dist/release-pins.d.ts.map +1 -0
- package/dist/release-pins.js +27 -0
- package/dist/release-pins.js.map +1 -0
- package/dist/release-public-key.d.ts +8 -0
- package/dist/release-public-key.d.ts.map +1 -0
- package/dist/release-public-key.js +13 -0
- package/dist/release-public-key.js.map +1 -0
- package/dist/restore-system-relation-targets.d.ts +3 -0
- package/dist/restore-system-relation-targets.d.ts.map +1 -0
- package/dist/restore-system-relation-targets.js +45 -0
- package/dist/restore-system-relation-targets.js.map +1 -0
- package/dist/runtime-routes.d.ts +34 -0
- package/dist/runtime-routes.d.ts.map +1 -0
- package/dist/runtime-routes.js +252 -0
- package/dist/runtime-routes.js.map +1 -0
- package/dist/schema-ast-v2.d.ts +127 -0
- package/dist/schema-ast-v2.d.ts.map +1 -0
- package/dist/schema-ast-v2.js +226 -0
- package/dist/schema-ast-v2.js.map +1 -0
- package/dist/scripts/postinstall.d.ts +11 -0
- package/dist/scripts/postinstall.d.ts.map +1 -0
- package/dist/scripts/postinstall.js +47 -0
- package/dist/scripts/postinstall.js.map +1 -0
- package/dist/seed.d.ts +8 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +32 -0
- package/dist/seed.js.map +1 -0
- package/dist/self-host-compose.d.ts +43 -0
- package/dist/self-host-compose.d.ts.map +1 -0
- package/dist/self-host-compose.js +400 -0
- package/dist/self-host-compose.js.map +1 -0
- package/dist/storage-provision.d.ts +24 -0
- package/dist/storage-provision.d.ts.map +1 -0
- package/dist/storage-provision.js +44 -0
- package/dist/storage-provision.js.map +1 -0
- package/dist/studio-admin-roles.d.ts +7 -0
- package/dist/studio-admin-roles.d.ts.map +1 -0
- package/dist/studio-admin-roles.js +14 -0
- package/dist/studio-admin-roles.js.map +1 -0
- package/dist/studio-dev-server.d.ts +22 -0
- package/dist/studio-dev-server.d.ts.map +1 -0
- package/dist/studio-dev-server.js +28 -0
- package/dist/studio-dev-server.js.map +1 -0
- package/dist/supatype-eval-1781522769253.d.mts +2 -0
- package/dist/supatype-eval-1781522769253.d.mts.map +1 -0
- package/dist/supatype-eval-1781522769253.mjs +3 -0
- package/dist/supatype-eval-1781522769253.mjs.map +1 -0
- package/dist/systemd.d.ts +26 -0
- package/dist/systemd.d.ts.map +1 -0
- package/dist/systemd.js +102 -0
- package/dist/systemd.js.map +1 -0
- package/dist/tsx-runner.d.ts +18 -0
- package/dist/tsx-runner.d.ts.map +1 -0
- package/dist/tsx-runner.js +69 -0
- package/dist/tsx-runner.js.map +1 -0
- package/dist/type-extractor.d.ts +4 -0
- package/dist/type-extractor.d.ts.map +1 -0
- package/dist/type-extractor.js +1213 -0
- package/dist/type-extractor.js.map +1 -0
- package/dist/type-resolver.d.ts +33 -0
- package/dist/type-resolver.d.ts.map +1 -0
- package/dist/type-resolver.js +338 -0
- package/dist/type-resolver.js.map +1 -0
- package/package.json +41 -0
- package/releases/deno/VERSION +1 -0
- package/scripts/mirror-deno-release.sh +76 -0
- package/src/TYPE-RESOLUTION.md +294 -0
- package/src/app/framework.ts +249 -0
- package/src/app/proxy-dev-app.ts +68 -0
- package/src/app-config.ts +128 -0
- package/src/augmentation-generator.ts +126 -0
- package/src/binary-cache.ts +845 -0
- package/src/cli.ts +63 -0
- package/src/commands/admin.ts +372 -0
- package/src/commands/app.ts +97 -0
- package/src/commands/cache.ts +117 -0
- package/src/commands/cloud.ts +325 -0
- package/src/commands/db.ts +136 -0
- package/src/commands/deploy-types.ts +49 -0
- package/src/commands/deploy.ts +400 -0
- package/src/commands/dev.ts +1009 -0
- package/src/commands/diff.ts +63 -0
- package/src/commands/engine.ts +30 -0
- package/src/commands/functions.ts +901 -0
- package/src/commands/generate.ts +44 -0
- package/src/commands/init.ts +253 -0
- package/src/commands/keys.ts +66 -0
- package/src/commands/logs.ts +58 -0
- package/src/commands/migrate-from-v1.ts +131 -0
- package/src/commands/migrate.ts +87 -0
- package/src/commands/pg.ts +133 -0
- package/src/commands/plugins.ts +508 -0
- package/src/commands/pull.ts +17 -0
- package/src/commands/push.ts +226 -0
- package/src/commands/seed.ts +68 -0
- package/src/commands/self-host.ts +364 -0
- package/src/commands/self-update.ts +45 -0
- package/src/commands/status.ts +84 -0
- package/src/commands/types.ts +76 -0
- package/src/commands/update.ts +136 -0
- package/src/components.ts +6 -0
- package/src/config.ts +223 -0
- package/src/dev-compose.ts +583 -0
- package/src/dev-log-bus.ts +101 -0
- package/src/dev-log-filter.ts +32 -0
- package/src/dev-logo.ts +62 -0
- package/src/dev-session.ts +130 -0
- package/src/dev-shutdown.ts +54 -0
- package/src/dev-task-colors.ts +47 -0
- package/src/dev-tui.ts +232 -0
- package/src/diff-output.ts +12 -0
- package/src/docker-postgres.ts +295 -0
- package/src/engine-client.ts +236 -0
- package/src/engine-push-output.ts +71 -0
- package/src/ensure-binary.ts +28 -0
- package/src/functions-router-gen.ts +224 -0
- package/src/index.ts +11 -0
- package/src/jwt.ts +14 -0
- package/src/kong-config.ts +93 -0
- package/src/local-gateway.ts +9 -0
- package/src/local-storage.ts +14 -0
- package/src/pgbouncer-userlist.ts +15 -0
- package/src/postgres-ctl.ts +171 -0
- package/src/process-manager.ts +220 -0
- package/src/project-config.ts +388 -0
- package/src/pull-utils.ts +81 -0
- package/src/release-pins.ts +31 -0
- package/src/release-public-key.ts +12 -0
- package/src/restore-system-relation-targets.ts +45 -0
- package/src/runtime-routes.ts +291 -0
- package/src/schema-ast-v2.ts +324 -0
- package/src/scripts/postinstall.ts +51 -0
- package/src/seed.ts +43 -0
- package/src/self-host-compose.ts +452 -0
- package/src/storage-provision.ts +58 -0
- package/src/studio-admin-roles.ts +16 -0
- package/src/studio-dev-server.ts +53 -0
- package/src/supatype-eval-1781522769253.mts +1 -0
- package/src/systemd.ts +137 -0
- package/src/tsx-runner.ts +89 -0
- package/src/type-extractor.ts +1479 -0
- package/src/type-resolver.ts +457 -0
- package/tests/app-command.test.ts +54 -0
- package/tests/augmentation-generator.test.ts +59 -0
- package/tests/binary-cache-cloud-overrides.test.ts +123 -0
- package/tests/cached-artifact-format.test.ts +84 -0
- package/tests/cli-help.test.ts +133 -0
- package/tests/config.test.ts +252 -0
- package/tests/dev-ui.test.ts +139 -0
- package/tests/docker-postgres.test.ts +39 -0
- package/tests/engine-distribution.test.ts +418 -0
- package/tests/engine-push-output.test.ts +67 -0
- package/tests/ensure-binary.test.ts +59 -0
- package/tests/init.test.ts +127 -0
- package/tests/keys.test.ts +160 -0
- package/tests/migrate-from-v1.test.ts +29 -0
- package/tests/normalize-admin-config.test.ts +48 -0
- package/tests/pg-spawn-env.test.ts +18 -0
- package/tests/postgres-archive-tag.test.ts +9 -0
- package/tests/proxy-dev-app.test.ts +33 -0
- package/tests/pull-utils.test.ts +150 -0
- package/tests/release-pins.test.ts +28 -0
- package/tests/runtime-contract.test.ts +370 -0
- package/tests/seed-discover.test.ts +31 -0
- package/tests/studio-admin-roles.test.ts +27 -0
- package/tests/tsconfig.json +9 -0
- package/tests/tsx-runner.test.ts +66 -0
- package/tests/type-extractor.test.ts +985 -0
- package/tests/type-resolver.test.ts +59 -0
- package/tsconfig.json +10 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +12 -0
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync, unlinkSync, } from "node:fs";
|
|
2
|
+
import { resolve, join, basename, relative, isAbsolute } from "node:path";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { localKongBaseUrl } from "../local-gateway.js";
|
|
5
|
+
import { loadConfig } from "../config.js";
|
|
6
|
+
import { ensureBinary } from "../ensure-binary.js";
|
|
7
|
+
import { functionsPathCandidatesFromProject, preferredFunctionsPathFromProject, } from "../project-config.js";
|
|
8
|
+
import { discoverTsFunctionsInDir, generateFunctionsRouterSource, } from "../functions-router-gen.js";
|
|
9
|
+
import { selfHostComposePaths } from "../self-host-compose.js";
|
|
10
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
11
|
+
const SHARED_DIR = "_shared";
|
|
12
|
+
const ENV_LOCAL = ".env.local";
|
|
13
|
+
const ENV_PRODUCTION = ".env.production";
|
|
14
|
+
// ─── Registration ────────────────────────────────────────────────────────────
|
|
15
|
+
export function registerFunctions(program) {
|
|
16
|
+
const fnCmd = program
|
|
17
|
+
.command("functions")
|
|
18
|
+
.description("Manage Supatype Edge Functions (Deno-based serverless TypeScript)");
|
|
19
|
+
fnCmd
|
|
20
|
+
.command("new <name>")
|
|
21
|
+
.description("Scaffold a new edge function")
|
|
22
|
+
.action((name) => {
|
|
23
|
+
scaffoldFunction(process.cwd(), name);
|
|
24
|
+
});
|
|
25
|
+
fnCmd
|
|
26
|
+
.command("serve")
|
|
27
|
+
.description("Start a local Deno server that serves all functions with hot reload")
|
|
28
|
+
.option("--port <port>", "Port to serve on", "54321")
|
|
29
|
+
.option("--env-file <path>", "Path to env file", ENV_LOCAL)
|
|
30
|
+
.action(async (opts) => {
|
|
31
|
+
await serve(process.cwd(), opts);
|
|
32
|
+
});
|
|
33
|
+
fnCmd
|
|
34
|
+
.command("deploy")
|
|
35
|
+
.description("Deploy all functions (or --only <name> for one) to the linked project")
|
|
36
|
+
.option("--only <name>", "Deploy a single function")
|
|
37
|
+
.option("--dry-run", "Show what would be deployed without deploying")
|
|
38
|
+
.action(async (opts) => {
|
|
39
|
+
await deploy(process.cwd(), opts);
|
|
40
|
+
});
|
|
41
|
+
fnCmd
|
|
42
|
+
.command("list")
|
|
43
|
+
.description("List all deployed functions for the linked project")
|
|
44
|
+
.action(async () => {
|
|
45
|
+
await listFunctions(process.cwd());
|
|
46
|
+
});
|
|
47
|
+
fnCmd
|
|
48
|
+
.command("delete <name>")
|
|
49
|
+
.description("Remove a deployed function")
|
|
50
|
+
.action(async (name) => {
|
|
51
|
+
await deleteFunction(process.cwd(), name);
|
|
52
|
+
});
|
|
53
|
+
fnCmd
|
|
54
|
+
.command("logs <name>")
|
|
55
|
+
.description("Tail logs for a deployed function")
|
|
56
|
+
.option("--since <duration>", "Show logs since duration (e.g. 1h, 30m)", "1h")
|
|
57
|
+
.action(async (name, opts) => {
|
|
58
|
+
await functionLogs(process.cwd(), name, opts);
|
|
59
|
+
});
|
|
60
|
+
fnCmd
|
|
61
|
+
.command("invoke <name>")
|
|
62
|
+
.description("Invoke a local or deployed function")
|
|
63
|
+
.option("--data <json>", "JSON body to send", "{}")
|
|
64
|
+
.option("--auth", "Include a test JWT in the request")
|
|
65
|
+
.option("--local", "Invoke the local dev server (default if serve is running)")
|
|
66
|
+
.action(async (name, opts) => {
|
|
67
|
+
await invoke(process.cwd(), name, opts);
|
|
68
|
+
});
|
|
69
|
+
const envCmd = fnCmd
|
|
70
|
+
.command("env")
|
|
71
|
+
.description("Manage function environment variables");
|
|
72
|
+
envCmd
|
|
73
|
+
.command("list")
|
|
74
|
+
.description("List environment variables (values masked)")
|
|
75
|
+
.action(async () => {
|
|
76
|
+
await envList(process.cwd());
|
|
77
|
+
});
|
|
78
|
+
envCmd
|
|
79
|
+
.command("set <keyvalue>")
|
|
80
|
+
.description("Set an environment variable (KEY=value)")
|
|
81
|
+
.action(async (keyvalue) => {
|
|
82
|
+
await envSet(process.cwd(), keyvalue);
|
|
83
|
+
});
|
|
84
|
+
envCmd
|
|
85
|
+
.command("unset <key>")
|
|
86
|
+
.description("Remove an environment variable")
|
|
87
|
+
.action(async (key) => {
|
|
88
|
+
await envUnset(process.cwd(), key);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
// ─── Scaffold ────────────────────────────────────────────────────────────────
|
|
92
|
+
function scaffoldFunction(cwd, name) {
|
|
93
|
+
const functionsDir = resolveFunctionsDir(cwd, "write");
|
|
94
|
+
const fnDir = resolve(functionsDir, name);
|
|
95
|
+
if (existsSync(fnDir)) {
|
|
96
|
+
console.error(`Function "${name}" already exists at ${relative(cwd, fnDir)}`);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
mkdirSync(fnDir, { recursive: true });
|
|
100
|
+
const indexContent = `// ${name} — Supatype Edge Function
|
|
101
|
+
// Docs: https://supatype.com/docs/edge-functions
|
|
102
|
+
|
|
103
|
+
export default async function handler(req: Request): Promise<Response> {
|
|
104
|
+
const { method, url } = req
|
|
105
|
+
|
|
106
|
+
// Example: read request body for POST requests
|
|
107
|
+
if (method === "POST") {
|
|
108
|
+
const body = await req.json()
|
|
109
|
+
return new Response(JSON.stringify({ message: "Hello from ${name}!", received: body }), {
|
|
110
|
+
status: 200,
|
|
111
|
+
headers: { "Content-Type": "application/json" },
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return new Response(JSON.stringify({ message: "Hello from ${name}!" }), {
|
|
116
|
+
status: 200,
|
|
117
|
+
headers: { "Content-Type": "application/json" },
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
`;
|
|
121
|
+
writeFileSync(join(fnDir, "index.ts"), indexContent, "utf8");
|
|
122
|
+
// Ensure _shared directory exists
|
|
123
|
+
const sharedDir = resolve(functionsDir, SHARED_DIR);
|
|
124
|
+
if (!existsSync(sharedDir)) {
|
|
125
|
+
mkdirSync(sharedDir, { recursive: true });
|
|
126
|
+
writeFileSync(join(sharedDir, "README.md"), "# Shared Code\n\nFiles in `_shared/` are available to all functions via relative imports.\nThis directory is not deployed as a function.\n\nExample: `import { sendEmail } from '../_shared/email.ts'`\n", "utf8");
|
|
127
|
+
}
|
|
128
|
+
// Ensure .env.local exists
|
|
129
|
+
const envLocalPath = resolve(functionsDir, ENV_LOCAL);
|
|
130
|
+
if (!existsSync(envLocalPath)) {
|
|
131
|
+
writeFileSync(envLocalPath, "# Local environment variables for edge functions\n# These are NOT committed to git\n# Set production env vars via: npx supatype functions env set KEY=value\n", "utf8");
|
|
132
|
+
}
|
|
133
|
+
const functionsDirLabel = relativeFunctionsDir(cwd, functionsDir);
|
|
134
|
+
console.log(`Created function: ${functionsDirLabel}/${name}/index.ts`);
|
|
135
|
+
console.log();
|
|
136
|
+
console.log(" Local dev: npx supatype functions serve");
|
|
137
|
+
console.log(` Invoke: npx supatype functions invoke ${name}`);
|
|
138
|
+
console.log(" Deploy: npx supatype functions deploy");
|
|
139
|
+
}
|
|
140
|
+
function resolveFunctionsDir(cwd, mode) {
|
|
141
|
+
try {
|
|
142
|
+
const cfg = loadConfig(cwd);
|
|
143
|
+
if (mode === "write") {
|
|
144
|
+
return preferredFunctionsPathFromProject(cfg, cwd);
|
|
145
|
+
}
|
|
146
|
+
const candidates = functionsPathCandidatesFromProject(cfg, cwd);
|
|
147
|
+
return candidates.find(dir => existsSync(dir)) ?? candidates[0] ?? resolve(cwd, "functions");
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// Keep commands usable even if config cannot be loaded yet.
|
|
151
|
+
const modern = resolve(cwd, "functions");
|
|
152
|
+
const legacy = resolve(cwd, "supatype/functions");
|
|
153
|
+
if (mode === "write")
|
|
154
|
+
return existsSync(legacy) ? legacy : modern;
|
|
155
|
+
return existsSync(modern) ? modern : legacy;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function relativeFunctionsDir(cwd, functionsDir) {
|
|
159
|
+
const rel = relative(cwd, functionsDir);
|
|
160
|
+
return rel.length > 0 ? rel : ".";
|
|
161
|
+
}
|
|
162
|
+
function resolveEnvFilePath(cwd, functionsDir, envFile) {
|
|
163
|
+
if (isAbsolute(envFile))
|
|
164
|
+
return envFile;
|
|
165
|
+
if (envFile.includes("/") || envFile.includes("\\"))
|
|
166
|
+
return resolve(cwd, envFile);
|
|
167
|
+
return resolve(functionsDir, envFile);
|
|
168
|
+
}
|
|
169
|
+
function discoverFunctions(cwd) {
|
|
170
|
+
const functionsDir = resolveFunctionsDir(cwd, "read");
|
|
171
|
+
if (!existsSync(functionsDir))
|
|
172
|
+
return [];
|
|
173
|
+
const entries = readdirSync(functionsDir);
|
|
174
|
+
const fns = [];
|
|
175
|
+
for (const entry of entries) {
|
|
176
|
+
if (entry.startsWith("_") || entry.startsWith("."))
|
|
177
|
+
continue;
|
|
178
|
+
const fullPath = join(functionsDir, entry);
|
|
179
|
+
const stat = statSync(fullPath);
|
|
180
|
+
if (stat.isDirectory()) {
|
|
181
|
+
// Directory function — look for index.ts
|
|
182
|
+
const indexPath = join(fullPath, "index.ts");
|
|
183
|
+
if (existsSync(indexPath)) {
|
|
184
|
+
fns.push({ name: entry, entrypoint: indexPath, absPath: fullPath });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else if (entry.endsWith(".ts") && !entry.endsWith(".d.ts")) {
|
|
188
|
+
// Single-file function
|
|
189
|
+
const name = basename(entry, ".ts");
|
|
190
|
+
fns.push({ name, entrypoint: fullPath, absPath: fullPath });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return fns.sort((a, b) => a.name.localeCompare(b.name));
|
|
194
|
+
}
|
|
195
|
+
// ─── Serve (local dev) ──────────────────────────────────────────────────────
|
|
196
|
+
async function serve(cwd, opts) {
|
|
197
|
+
const config = loadConfig(cwd);
|
|
198
|
+
const functionsDir = resolveFunctionsDir(cwd, "read");
|
|
199
|
+
const functionsDirLabel = relativeFunctionsDir(cwd, functionsDir);
|
|
200
|
+
const routes = discoverTsFunctionsInDir(functionsDir);
|
|
201
|
+
if (routes.length === 0) {
|
|
202
|
+
console.error(`No functions found in ${functionsDirLabel}/`);
|
|
203
|
+
console.error("Create one with: npx supatype functions new <name>");
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
console.log(`Discovered ${routes.length} function(s):`);
|
|
207
|
+
for (const fn of routes) {
|
|
208
|
+
console.log(` /${fn.name} → ${relative(cwd, fn.entrypoint)}`);
|
|
209
|
+
}
|
|
210
|
+
console.log();
|
|
211
|
+
// Generate a Deno entry script that routes requests to the correct function
|
|
212
|
+
const routerPath = resolve(functionsDir, ".serve-router.ts");
|
|
213
|
+
const routerScript = generateFunctionsRouterSource(routerPath, routes);
|
|
214
|
+
writeFileSync(routerPath, routerScript, "utf8");
|
|
215
|
+
const envFilePath = resolveEnvFilePath(cwd, functionsDir, opts.envFile);
|
|
216
|
+
const envArgs = [];
|
|
217
|
+
if (existsSync(envFilePath)) {
|
|
218
|
+
envArgs.push("--env-file", envFilePath);
|
|
219
|
+
}
|
|
220
|
+
console.log(`Serving functions at http://localhost:${opts.port}/functions/v1/`);
|
|
221
|
+
console.log("Watching for changes...\n");
|
|
222
|
+
let denoBin;
|
|
223
|
+
try {
|
|
224
|
+
denoBin = await ensureBinary("deno", config);
|
|
225
|
+
}
|
|
226
|
+
catch (err) {
|
|
227
|
+
console.error(`[supatype] Could not provision Deno: ${err.message}`);
|
|
228
|
+
process.exit(1);
|
|
229
|
+
}
|
|
230
|
+
const result = spawnSync(denoBin, [
|
|
231
|
+
"run",
|
|
232
|
+
"--allow-net",
|
|
233
|
+
"--allow-env",
|
|
234
|
+
"--allow-read",
|
|
235
|
+
"--watch",
|
|
236
|
+
...envArgs,
|
|
237
|
+
routerPath,
|
|
238
|
+
], {
|
|
239
|
+
stdio: "inherit",
|
|
240
|
+
cwd,
|
|
241
|
+
env: {
|
|
242
|
+
...process.env,
|
|
243
|
+
PORT: opts.port,
|
|
244
|
+
SUPATYPE_DENO_FUNCTIONS_DIR: functionsDir,
|
|
245
|
+
SUPATYPE_SHARED_ENV_FILE: resolve(functionsDir, ENV_LOCAL),
|
|
246
|
+
SUPATYPE_URL: process.env["SUPATYPE_URL"] ?? localKongBaseUrl(),
|
|
247
|
+
SUPATYPE_ANON_KEY: process.env["SUPATYPE_ANON_KEY"] ?? "",
|
|
248
|
+
SUPATYPE_SERVICE_ROLE_KEY: process.env["SUPATYPE_SERVICE_ROLE_KEY"] ?? "",
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
// Clean up router script
|
|
252
|
+
try {
|
|
253
|
+
unlinkSync(routerPath);
|
|
254
|
+
}
|
|
255
|
+
catch { /* ignore */ }
|
|
256
|
+
if (result.status !== 0) {
|
|
257
|
+
console.error("Function server exited with errors.");
|
|
258
|
+
process.exit(result.status ?? 1);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// ─── Deploy ──────────────────────────────────────────────────────────────────
|
|
262
|
+
async function deploy(cwd, opts) {
|
|
263
|
+
const allFns = discoverFunctions(cwd);
|
|
264
|
+
const fns = opts.only
|
|
265
|
+
? allFns.filter(f => f.name === opts.only)
|
|
266
|
+
: allFns;
|
|
267
|
+
if (fns.length === 0) {
|
|
268
|
+
const functionsDir = resolveFunctionsDir(cwd, "read");
|
|
269
|
+
const functionsDirLabel = relativeFunctionsDir(cwd, functionsDir);
|
|
270
|
+
if (opts.only) {
|
|
271
|
+
console.error(`Function "${opts.only}" not found in ${functionsDirLabel}/`);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
console.error(`No functions found in ${functionsDirLabel}/`);
|
|
275
|
+
}
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
if (opts.dryRun) {
|
|
279
|
+
console.log("Dry run — the following functions would be deployed:\n");
|
|
280
|
+
for (const fn of fns) {
|
|
281
|
+
console.log(` ${fn.name} → ${relative(cwd, fn.entrypoint)}`);
|
|
282
|
+
}
|
|
283
|
+
console.log(`\nTotal: ${fns.length} function(s)`);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const composePath = selfHostComposePaths(cwd).composePath;
|
|
287
|
+
if (existsSync(composePath)) {
|
|
288
|
+
await deploySelfHosted(cwd, fns);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
await deployCloud(cwd, fns);
|
|
292
|
+
}
|
|
293
|
+
async function deploySelfHosted(cwd, fns) {
|
|
294
|
+
console.log("Self-host Compose deployment.\n");
|
|
295
|
+
console.log("Functions are served from your project functions/ directory (no bundle step).\n");
|
|
296
|
+
for (const fn of fns) {
|
|
297
|
+
console.log(` ${fn.name} → ${relative(cwd, fn.entrypoint)}`);
|
|
298
|
+
}
|
|
299
|
+
console.log(`\n${fns.length} function(s) ready on disk.`);
|
|
300
|
+
console.log("Restart the functions-worker container to load changes:");
|
|
301
|
+
console.log(" supatype self-host compose restart functions-worker");
|
|
302
|
+
console.log("\nKong → supatype-server → functions-worker (per-project worker).");
|
|
303
|
+
}
|
|
304
|
+
async function deployCloud(cwd, fns) {
|
|
305
|
+
const { getLinkedProject, getCloudToken, getCloudApiUrl } = await loadCloudHelpers();
|
|
306
|
+
const linked = getLinkedProject(cwd);
|
|
307
|
+
if (!linked) {
|
|
308
|
+
console.error("No linked project. Run: npx supatype cloud link");
|
|
309
|
+
process.exit(1);
|
|
310
|
+
}
|
|
311
|
+
const token = getCloudToken();
|
|
312
|
+
if (!token) {
|
|
313
|
+
console.error("Not logged in. Run: npx supatype cloud login");
|
|
314
|
+
process.exit(1);
|
|
315
|
+
}
|
|
316
|
+
const apiUrl = getCloudApiUrl();
|
|
317
|
+
console.log(`Deploying to project: ${linked.ref}\n`);
|
|
318
|
+
for (const fn of fns) {
|
|
319
|
+
const start = Date.now();
|
|
320
|
+
// Read source code
|
|
321
|
+
const source = readFunctionSource(fn);
|
|
322
|
+
try {
|
|
323
|
+
const res = await fetch(`${apiUrl}/api/v1/projects/${linked.ref}/functions/deploy`, {
|
|
324
|
+
method: "POST",
|
|
325
|
+
headers: {
|
|
326
|
+
Authorization: `Bearer ${token}`,
|
|
327
|
+
"Content-Type": "application/json",
|
|
328
|
+
"X-Org-Id": linked.orgId ?? "",
|
|
329
|
+
},
|
|
330
|
+
body: JSON.stringify({
|
|
331
|
+
functions: [{
|
|
332
|
+
name: fn.name,
|
|
333
|
+
source,
|
|
334
|
+
entrypoint: `${fn.name}/index.ts`,
|
|
335
|
+
}],
|
|
336
|
+
}),
|
|
337
|
+
signal: AbortSignal.timeout(60_000),
|
|
338
|
+
});
|
|
339
|
+
if (!res.ok) {
|
|
340
|
+
const body = await res.json().catch(() => ({}));
|
|
341
|
+
console.error(` ${fn.name} ✗ ${body["message"] ?? res.statusText}`);
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
const duration = Date.now() - start;
|
|
345
|
+
console.log(` ${fn.name} ✓ deployed (${duration}ms)`);
|
|
346
|
+
}
|
|
347
|
+
catch (err) {
|
|
348
|
+
console.error(` ${fn.name} ✗ ${err instanceof Error ? err.message : "unknown error"}`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
console.log(`\nDeployed ${fns.length} function(s)`);
|
|
352
|
+
console.log(`Invoke: https://${linked.ref}.supatype.dev/functions/v1/<name>`);
|
|
353
|
+
}
|
|
354
|
+
function readFunctionSource(fn) {
|
|
355
|
+
const stat = statSync(fn.absPath);
|
|
356
|
+
if (stat.isFile()) {
|
|
357
|
+
return readFileSync(fn.absPath, "utf8");
|
|
358
|
+
}
|
|
359
|
+
// Directory function — read all .ts files
|
|
360
|
+
const files = {};
|
|
361
|
+
const entries = readdirSync(fn.absPath, { recursive: true });
|
|
362
|
+
for (const entry of entries) {
|
|
363
|
+
const fullPath = join(fn.absPath, entry);
|
|
364
|
+
if (statSync(fullPath).isFile() && (entry.endsWith(".ts") || entry.endsWith(".js"))) {
|
|
365
|
+
files[entry] = readFileSync(fullPath, "utf8");
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return JSON.stringify(files);
|
|
369
|
+
}
|
|
370
|
+
// ─── List ────────────────────────────────────────────────────────────────────
|
|
371
|
+
async function listFunctions(cwd) {
|
|
372
|
+
const { getLinkedProject, getCloudToken, getCloudApiUrl } = await loadCloudHelpers();
|
|
373
|
+
const linked = getLinkedProject(cwd);
|
|
374
|
+
if (!linked) {
|
|
375
|
+
// Show local functions instead
|
|
376
|
+
const fns = discoverFunctions(cwd);
|
|
377
|
+
if (fns.length === 0) {
|
|
378
|
+
console.log("No functions found locally or remotely.");
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
console.log("Local functions (not linked to a cloud project):\n");
|
|
382
|
+
for (const fn of fns) {
|
|
383
|
+
console.log(` ${fn.name.padEnd(30)} ${relative(cwd, fn.entrypoint)}`);
|
|
384
|
+
}
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
const token = getCloudToken();
|
|
388
|
+
if (!token) {
|
|
389
|
+
console.error("Not logged in. Run: npx supatype cloud login");
|
|
390
|
+
process.exit(1);
|
|
391
|
+
}
|
|
392
|
+
try {
|
|
393
|
+
const res = await fetch(`${getCloudApiUrl()}/api/v1/projects/${linked.ref}/functions`, {
|
|
394
|
+
headers: {
|
|
395
|
+
Authorization: `Bearer ${token}`,
|
|
396
|
+
"X-Org-Id": linked.orgId ?? "",
|
|
397
|
+
},
|
|
398
|
+
signal: AbortSignal.timeout(10_000),
|
|
399
|
+
});
|
|
400
|
+
if (!res.ok) {
|
|
401
|
+
console.error(`Failed to list functions: ${res.statusText}`);
|
|
402
|
+
process.exit(1);
|
|
403
|
+
}
|
|
404
|
+
const { data } = await res.json();
|
|
405
|
+
if (data.length === 0) {
|
|
406
|
+
console.log("No deployed functions.");
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
console.log("Deployed functions:\n");
|
|
410
|
+
console.log(` ${"Name".padEnd(28)} ${"Last Deployed".padEnd(24)} ${"Invocations (24h)".padEnd(20)} Avg Duration`);
|
|
411
|
+
console.log(` ${"─".repeat(28)} ${"─".repeat(24)} ${"─".repeat(20)} ${"─".repeat(12)}`);
|
|
412
|
+
for (const fn of data) {
|
|
413
|
+
const deployed = fn.deployedAt ? new Date(fn.deployedAt).toLocaleString() : "—";
|
|
414
|
+
console.log(` ${fn.name.padEnd(28)} ${deployed.padEnd(24)} ${String(fn.invocations24h ?? 0).padEnd(20)} ${fn.avgDurationMs ?? 0}ms`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
catch (err) {
|
|
418
|
+
console.error(`Error: ${err instanceof Error ? err.message : "unknown"}`);
|
|
419
|
+
process.exit(1);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
// ─── Delete ──────────────────────────────────────────────────────────────────
|
|
423
|
+
async function deleteFunction(cwd, name) {
|
|
424
|
+
const { getLinkedProject, getCloudToken, getCloudApiUrl } = await loadCloudHelpers();
|
|
425
|
+
const linked = getLinkedProject(cwd);
|
|
426
|
+
if (!linked) {
|
|
427
|
+
console.error("No linked project. Run: npx supatype cloud link");
|
|
428
|
+
process.exit(1);
|
|
429
|
+
}
|
|
430
|
+
const token = getCloudToken();
|
|
431
|
+
if (!token) {
|
|
432
|
+
console.error("Not logged in. Run: npx supatype cloud login");
|
|
433
|
+
process.exit(1);
|
|
434
|
+
}
|
|
435
|
+
try {
|
|
436
|
+
const res = await fetch(`${getCloudApiUrl()}/api/v1/projects/${linked.ref}/functions/${name}`, {
|
|
437
|
+
method: "DELETE",
|
|
438
|
+
headers: {
|
|
439
|
+
Authorization: `Bearer ${token}`,
|
|
440
|
+
"X-Org-Id": linked.orgId ?? "",
|
|
441
|
+
},
|
|
442
|
+
signal: AbortSignal.timeout(10_000),
|
|
443
|
+
});
|
|
444
|
+
if (!res.ok) {
|
|
445
|
+
const body = await res.json().catch(() => ({}));
|
|
446
|
+
console.error(`Failed to delete "${name}": ${body["message"] ?? res.statusText}`);
|
|
447
|
+
process.exit(1);
|
|
448
|
+
}
|
|
449
|
+
console.log(`Function "${name}" deleted. It will return 404 immediately.`);
|
|
450
|
+
}
|
|
451
|
+
catch (err) {
|
|
452
|
+
console.error(`Error: ${err instanceof Error ? err.message : "unknown"}`);
|
|
453
|
+
process.exit(1);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
// ─── Logs ────────────────────────────────────────────────────────────────────
|
|
457
|
+
async function functionLogs(cwd, name, opts) {
|
|
458
|
+
const { getLinkedProject, getCloudToken, getCloudApiUrl } = await loadCloudHelpers();
|
|
459
|
+
const linked = getLinkedProject(cwd);
|
|
460
|
+
if (!linked) {
|
|
461
|
+
console.error("No linked project. Run: npx supatype cloud link");
|
|
462
|
+
process.exit(1);
|
|
463
|
+
}
|
|
464
|
+
const token = getCloudToken();
|
|
465
|
+
if (!token) {
|
|
466
|
+
console.error("Not logged in. Run: npx supatype cloud login");
|
|
467
|
+
process.exit(1);
|
|
468
|
+
}
|
|
469
|
+
try {
|
|
470
|
+
const res = await fetch(`${getCloudApiUrl()}/api/v1/projects/${linked.ref}/functions/${name}/logs?since=${opts.since}`, {
|
|
471
|
+
headers: {
|
|
472
|
+
Authorization: `Bearer ${token}`,
|
|
473
|
+
"X-Org-Id": linked.orgId ?? "",
|
|
474
|
+
},
|
|
475
|
+
signal: AbortSignal.timeout(10_000),
|
|
476
|
+
});
|
|
477
|
+
if (!res.ok) {
|
|
478
|
+
console.error(`Failed to fetch logs: ${res.statusText}`);
|
|
479
|
+
process.exit(1);
|
|
480
|
+
}
|
|
481
|
+
const { data } = await res.json();
|
|
482
|
+
if (data.length === 0) {
|
|
483
|
+
console.log(`No logs for "${name}" in the last ${opts.since}.`);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
for (const entry of data) {
|
|
487
|
+
const ts = new Date(entry.timestamp).toISOString().slice(11, 23);
|
|
488
|
+
const level = entry.level.toUpperCase().padEnd(5);
|
|
489
|
+
console.log(`${ts} [${level}] ${entry.message}`);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
catch (err) {
|
|
493
|
+
console.error(`Error: ${err instanceof Error ? err.message : "unknown"}`);
|
|
494
|
+
process.exit(1);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
// ─── Invoke ──────────────────────────────────────────────────────────────────
|
|
498
|
+
async function invoke(cwd, name, opts) {
|
|
499
|
+
let url;
|
|
500
|
+
const headers = { "Content-Type": "application/json" };
|
|
501
|
+
if (opts.local) {
|
|
502
|
+
url = `http://localhost:54321/functions/v1/${name}`;
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
const { getLinkedProject, getCloudToken } = await loadCloudHelpers();
|
|
506
|
+
const linked = getLinkedProject(cwd);
|
|
507
|
+
if (linked) {
|
|
508
|
+
url = `https://${linked.ref}.supatype.dev/functions/v1/${name}`;
|
|
509
|
+
const token = getCloudToken();
|
|
510
|
+
if (token && opts.auth) {
|
|
511
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
// Default to local
|
|
516
|
+
url = `http://localhost:54321/functions/v1/${name}`;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
if (opts.auth && !headers["Authorization"]) {
|
|
520
|
+
// Generate a test JWT for local invocation
|
|
521
|
+
headers["Authorization"] = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LXVzZXIiLCJyb2xlIjoiYXV0aGVudGljYXRlZCIsImlhdCI6MTcwMDAwMDAwMH0.test";
|
|
522
|
+
}
|
|
523
|
+
try {
|
|
524
|
+
let body;
|
|
525
|
+
try {
|
|
526
|
+
JSON.parse(opts.data);
|
|
527
|
+
body = opts.data;
|
|
528
|
+
}
|
|
529
|
+
catch {
|
|
530
|
+
console.error("Invalid JSON data. Use --data '{\"key\": \"value\"}'");
|
|
531
|
+
process.exit(1);
|
|
532
|
+
}
|
|
533
|
+
const start = Date.now();
|
|
534
|
+
const res = await fetch(url, {
|
|
535
|
+
method: "POST",
|
|
536
|
+
headers,
|
|
537
|
+
body,
|
|
538
|
+
signal: AbortSignal.timeout(30_000),
|
|
539
|
+
});
|
|
540
|
+
const duration = Date.now() - start;
|
|
541
|
+
const responseBody = await res.text();
|
|
542
|
+
console.log(`Status: ${res.status} (${duration}ms)`);
|
|
543
|
+
console.log();
|
|
544
|
+
// Try to pretty-print JSON
|
|
545
|
+
try {
|
|
546
|
+
const json = JSON.parse(responseBody);
|
|
547
|
+
console.log(JSON.stringify(json, null, 2));
|
|
548
|
+
}
|
|
549
|
+
catch {
|
|
550
|
+
console.log(responseBody);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
catch (err) {
|
|
554
|
+
if (err instanceof TypeError && err.message.includes("fetch")) {
|
|
555
|
+
console.error(`Cannot reach ${url}`);
|
|
556
|
+
console.error("Is the function server running? Start it with: npx supatype functions serve");
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
console.error(`Error: ${err instanceof Error ? err.message : "unknown"}`);
|
|
560
|
+
}
|
|
561
|
+
process.exit(1);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
// ─── Env management ──────────────────────────────────────────────────────────
|
|
565
|
+
async function envList(cwd) {
|
|
566
|
+
const { getLinkedProject, getCloudToken, getCloudApiUrl } = await loadCloudHelpers();
|
|
567
|
+
const linked = getLinkedProject(cwd);
|
|
568
|
+
if (!linked) {
|
|
569
|
+
// Show local env vars
|
|
570
|
+
const envPath = resolve(resolveFunctionsDir(cwd, "read"), ENV_LOCAL);
|
|
571
|
+
if (!existsSync(envPath)) {
|
|
572
|
+
console.log("No environment variables configured.");
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
const lines = readFileSync(envPath, "utf8").split("\n");
|
|
576
|
+
console.log("Local environment variables:\n");
|
|
577
|
+
for (const line of lines) {
|
|
578
|
+
const trimmed = line.trim();
|
|
579
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
580
|
+
continue;
|
|
581
|
+
const eqIdx = trimmed.indexOf("=");
|
|
582
|
+
if (eqIdx > 0) {
|
|
583
|
+
const key = trimmed.slice(0, eqIdx);
|
|
584
|
+
console.log(` ${key} = ••••••••`);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
const token = getCloudToken();
|
|
590
|
+
if (!token) {
|
|
591
|
+
console.error("Not logged in. Run: npx supatype cloud login");
|
|
592
|
+
process.exit(1);
|
|
593
|
+
}
|
|
594
|
+
try {
|
|
595
|
+
const res = await fetch(`${getCloudApiUrl()}/api/v1/projects/${linked.ref}/functions/env`, {
|
|
596
|
+
headers: {
|
|
597
|
+
Authorization: `Bearer ${token}`,
|
|
598
|
+
"X-Org-Id": linked.orgId ?? "",
|
|
599
|
+
},
|
|
600
|
+
signal: AbortSignal.timeout(10_000),
|
|
601
|
+
});
|
|
602
|
+
if (!res.ok) {
|
|
603
|
+
console.error(`Failed to list env vars: ${res.statusText}`);
|
|
604
|
+
process.exit(1);
|
|
605
|
+
}
|
|
606
|
+
const { data } = await res.json();
|
|
607
|
+
if (data.length === 0) {
|
|
608
|
+
console.log("No environment variables set.");
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
console.log("Environment variables (values masked):\n");
|
|
612
|
+
for (const key of data) {
|
|
613
|
+
console.log(` ${key} = ••••••••`);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
catch (err) {
|
|
617
|
+
console.error(`Error: ${err instanceof Error ? err.message : "unknown"}`);
|
|
618
|
+
process.exit(1);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
async function envSet(cwd, keyvalue) {
|
|
622
|
+
const eqIdx = keyvalue.indexOf("=");
|
|
623
|
+
if (eqIdx <= 0) {
|
|
624
|
+
console.error("Invalid format. Use: npx supatype functions env set KEY=value");
|
|
625
|
+
process.exit(1);
|
|
626
|
+
}
|
|
627
|
+
const key = keyvalue.slice(0, eqIdx);
|
|
628
|
+
const value = keyvalue.slice(eqIdx + 1);
|
|
629
|
+
const { getLinkedProject, getCloudToken, getCloudApiUrl } = await loadCloudHelpers();
|
|
630
|
+
const linked = getLinkedProject(cwd);
|
|
631
|
+
if (!linked) {
|
|
632
|
+
// Set in local env file
|
|
633
|
+
const envPath = resolve(resolveFunctionsDir(cwd, "write"), ENV_LOCAL);
|
|
634
|
+
let content = existsSync(envPath) ? readFileSync(envPath, "utf8") : "";
|
|
635
|
+
// Replace existing or append
|
|
636
|
+
const regex = new RegExp(`^${key}=.*$`, "m");
|
|
637
|
+
if (regex.test(content)) {
|
|
638
|
+
content = content.replace(regex, `${key}=${value}`);
|
|
639
|
+
}
|
|
640
|
+
else {
|
|
641
|
+
content = content.trimEnd() + `\n${key}=${value}\n`;
|
|
642
|
+
}
|
|
643
|
+
writeFileSync(envPath, content, "utf8");
|
|
644
|
+
console.log(`Set ${key} in local env file.`);
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
const token = getCloudToken();
|
|
648
|
+
if (!token) {
|
|
649
|
+
console.error("Not logged in. Run: npx supatype cloud login");
|
|
650
|
+
process.exit(1);
|
|
651
|
+
}
|
|
652
|
+
try {
|
|
653
|
+
const res = await fetch(`${getCloudApiUrl()}/api/v1/projects/${linked.ref}/functions/env`, {
|
|
654
|
+
method: "POST",
|
|
655
|
+
headers: {
|
|
656
|
+
Authorization: `Bearer ${token}`,
|
|
657
|
+
"Content-Type": "application/json",
|
|
658
|
+
"X-Org-Id": linked.orgId ?? "",
|
|
659
|
+
},
|
|
660
|
+
body: JSON.stringify({ key, value }),
|
|
661
|
+
signal: AbortSignal.timeout(10_000),
|
|
662
|
+
});
|
|
663
|
+
if (!res.ok) {
|
|
664
|
+
const body = await res.json().catch(() => ({}));
|
|
665
|
+
console.error(`Failed to set env var: ${body["message"] ?? res.statusText}`);
|
|
666
|
+
process.exit(1);
|
|
667
|
+
}
|
|
668
|
+
console.log(`Set ${key} for project ${linked.ref}.`);
|
|
669
|
+
}
|
|
670
|
+
catch (err) {
|
|
671
|
+
console.error(`Error: ${err instanceof Error ? err.message : "unknown"}`);
|
|
672
|
+
process.exit(1);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
async function envUnset(cwd, key) {
|
|
676
|
+
const { getLinkedProject, getCloudToken, getCloudApiUrl } = await loadCloudHelpers();
|
|
677
|
+
const linked = getLinkedProject(cwd);
|
|
678
|
+
if (!linked) {
|
|
679
|
+
const envPath = resolve(resolveFunctionsDir(cwd, "read"), ENV_LOCAL);
|
|
680
|
+
if (!existsSync(envPath)) {
|
|
681
|
+
console.error("No local env file found.");
|
|
682
|
+
process.exit(1);
|
|
683
|
+
}
|
|
684
|
+
let content = readFileSync(envPath, "utf8");
|
|
685
|
+
const regex = new RegExp(`^${key}=.*\n?`, "m");
|
|
686
|
+
content = content.replace(regex, "");
|
|
687
|
+
writeFileSync(envPath, content, "utf8");
|
|
688
|
+
console.log(`Removed ${key} from local env file.`);
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
const token = getCloudToken();
|
|
692
|
+
if (!token) {
|
|
693
|
+
console.error("Not logged in. Run: npx supatype cloud login");
|
|
694
|
+
process.exit(1);
|
|
695
|
+
}
|
|
696
|
+
try {
|
|
697
|
+
const res = await fetch(`${getCloudApiUrl()}/api/v1/projects/${linked.ref}/functions/env/${key}`, {
|
|
698
|
+
method: "DELETE",
|
|
699
|
+
headers: {
|
|
700
|
+
Authorization: `Bearer ${token}`,
|
|
701
|
+
"X-Org-Id": linked.orgId ?? "",
|
|
702
|
+
},
|
|
703
|
+
signal: AbortSignal.timeout(10_000),
|
|
704
|
+
});
|
|
705
|
+
if (!res.ok) {
|
|
706
|
+
const body = await res.json().catch(() => ({}));
|
|
707
|
+
console.error(`Failed to unset env var: ${body["message"] ?? res.statusText}`);
|
|
708
|
+
process.exit(1);
|
|
709
|
+
}
|
|
710
|
+
console.log(`Removed ${key} for project ${linked.ref}.`);
|
|
711
|
+
}
|
|
712
|
+
catch (err) {
|
|
713
|
+
console.error(`Error: ${err instanceof Error ? err.message : "unknown"}`);
|
|
714
|
+
process.exit(1);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
async function loadCloudHelpers() {
|
|
718
|
+
// These helpers read the local .supatype/linked.json and auth token
|
|
719
|
+
return {
|
|
720
|
+
getLinkedProject(cwd) {
|
|
721
|
+
const linkedPath = resolve(cwd, ".supatype/linked.json");
|
|
722
|
+
if (!existsSync(linkedPath))
|
|
723
|
+
return null;
|
|
724
|
+
try {
|
|
725
|
+
const data = JSON.parse(readFileSync(linkedPath, "utf8"));
|
|
726
|
+
const ref = data["ref"];
|
|
727
|
+
const orgId = data["orgId"];
|
|
728
|
+
return ref ? { ref, ...(orgId !== undefined ? { orgId } : {}) } : null;
|
|
729
|
+
}
|
|
730
|
+
catch {
|
|
731
|
+
return null;
|
|
732
|
+
}
|
|
733
|
+
},
|
|
734
|
+
getCloudToken() {
|
|
735
|
+
// Check env first, then config file
|
|
736
|
+
if (process.env["SUPATYPE_ACCESS_TOKEN"]) {
|
|
737
|
+
return process.env["SUPATYPE_ACCESS_TOKEN"];
|
|
738
|
+
}
|
|
739
|
+
const tokenPath = resolve(process.env["HOME"] ?? process.env["USERPROFILE"] ?? "~", ".supatype/token");
|
|
740
|
+
if (!existsSync(tokenPath))
|
|
741
|
+
return null;
|
|
742
|
+
return readFileSync(tokenPath, "utf8").trim() || null;
|
|
743
|
+
},
|
|
744
|
+
getCloudApiUrl() {
|
|
745
|
+
return process.env["SUPATYPE_API_URL"] ?? "https://api.supatype.com";
|
|
746
|
+
},
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
//# sourceMappingURL=functions.js.map
|