diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..c2658d7d1 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/ai-assistants/mod.ts b/ai-assistants/mod.ts index 585b4d471..e48b999c9 100644 --- a/ai-assistants/mod.ts +++ b/ai-assistants/mod.ts @@ -1,5 +1,6 @@ +import { env } from "../compat/runtime/mod.ts"; import AWS from "npm:aws-sdk@2.1585.0"; -import { deferred } from "std/async/deferred.ts"; +import { deferred } from "../compat/runtime/async.ts"; import openai, { Props as OpenAIProps, State as OpenAIState, @@ -170,11 +171,11 @@ export default function App(state: Props): App extends Promise { + resolve(value: T | PromiseLike): void; + reject(reason?: unknown): void; + readonly state: "pending" | "fulfilled" | "rejected"; +} + +export function deferred(): Deferred { + let methods: { + resolve: (value: T | PromiseLike) => void; + reject: (reason?: unknown) => void; + }; + + let state: "pending" | "fulfilled" | "rejected" = "pending"; + + const promise = new Promise((resolve, reject) => { + methods = { + resolve: (value) => { + state = "fulfilled"; + resolve(value); + }, + reject: (reason) => { + state = "rejected"; + reject(reason); + }, + }; + }) as Deferred; + + Object.defineProperty(promise, "state", { + get: () => state, + }); + + return Object.assign(promise, methods!); +} + +export const delay = (ms: number): Promise => + new Promise((resolve) => setTimeout(resolve, ms)); + diff --git a/compat/runtime/colors.ts b/compat/runtime/colors.ts new file mode 100644 index 000000000..ecac2f5da --- /dev/null +++ b/compat/runtime/colors.ts @@ -0,0 +1,25 @@ +// compat/runtime/colors.ts +// ANSI color codes - works in all terminals +// Replacement for std/fmt/colors.ts + +export const red = (s: string): string => `\x1b[31m${s}\x1b[0m`; +export const green = (s: string): string => `\x1b[32m${s}\x1b[0m`; +export const yellow = (s: string): string => `\x1b[33m${s}\x1b[0m`; +export const blue = (s: string): string => `\x1b[34m${s}\x1b[0m`; +export const magenta = (s: string): string => `\x1b[35m${s}\x1b[0m`; +export const cyan = (s: string): string => `\x1b[36m${s}\x1b[0m`; +export const white = (s: string): string => `\x1b[37m${s}\x1b[0m`; +export const gray = (s: string): string => `\x1b[90m${s}\x1b[0m`; +export const brightRed = (s: string): string => `\x1b[91m${s}\x1b[0m`; +export const brightGreen = (s: string): string => `\x1b[92m${s}\x1b[0m`; +export const brightYellow = (s: string): string => `\x1b[93m${s}\x1b[0m`; +export const brightBlue = (s: string): string => `\x1b[94m${s}\x1b[0m`; +export const brightMagenta = (s: string): string => `\x1b[95m${s}\x1b[0m`; +export const brightCyan = (s: string): string => `\x1b[96m${s}\x1b[0m`; +export const bold = (s: string): string => `\x1b[1m${s}\x1b[0m`; +export const dim = (s: string): string => `\x1b[2m${s}\x1b[0m`; +export const italic = (s: string): string => `\x1b[3m${s}\x1b[0m`; +export const underline = (s: string): string => `\x1b[4m${s}\x1b[0m`; +export const stripColor = (s: string): string => + s.replace(/\x1b\[[0-9;]*m/g, ""); + diff --git a/compat/runtime/env.ts b/compat/runtime/env.ts new file mode 100644 index 000000000..4f3e03982 --- /dev/null +++ b/compat/runtime/env.ts @@ -0,0 +1,37 @@ +// compat/runtime/env.ts +// Environment variables abstraction for apps + +declare const Deno: { + env: { + get(key: string): string | undefined; + has(key: string): boolean; + set(key: string, value: string): void; + }; +} | undefined; + +const isDeno = typeof Deno !== "undefined"; + +export const env = { + get: (key: string): string | undefined => { + if (isDeno) { + return Deno!.env.get(key); + } + return process.env[key]; + }, + + has: (key: string): boolean => { + if (isDeno) { + return Deno!.env.has(key); + } + return key in process.env && process.env[key] !== undefined; + }, + + set: (key: string, value: string): void => { + if (isDeno) { + Deno!.env.set(key, value); + } else { + process.env[key] = value; + } + }, +}; + diff --git a/compat/runtime/inspect.ts b/compat/runtime/inspect.ts new file mode 100644 index 000000000..17f359f9a --- /dev/null +++ b/compat/runtime/inspect.ts @@ -0,0 +1,57 @@ +// compat/runtime/inspect.ts +// Object inspection abstraction for apps + +declare const Deno: { + inspect(value: unknown, options?: { depth?: number; colors?: boolean }): string; +} | undefined; + +const isDeno = typeof Deno !== "undefined"; + +// Cache the util module for Node.js +let nodeUtil: { inspect: (value: unknown, options?: object) => string } | null = null; + +const getNodeUtil = async () => { + if (nodeUtil) return nodeUtil; + try { + nodeUtil = await import("node:util"); + return nodeUtil; + } catch { + return null; + } +}; + +// Preload util module in Node.js +if (!isDeno) { + getNodeUtil(); +} + +export const inspect = ( + value: unknown, + options?: { depth?: number; colors?: boolean }, +): string => { + if (isDeno) { + return Deno!.inspect(value, options); + } + + // Node.js / Bun - use cached module if available + if (nodeUtil?.inspect) { + return nodeUtil.inspect(value, { + depth: options?.depth ?? 4, + colors: options?.colors ?? false, + }); + } + + // Fallback for cases where module isn't loaded yet + // Handle errors specially + if (value instanceof Error) { + return `${value.name}: ${value.message}${value.stack ? `\n${value.stack}` : ""}`; + } + + // Ultimate fallback + try { + return JSON.stringify(value, null, 2); + } catch { + return String(value); + } +}; + diff --git a/compat/runtime/media-types.ts b/compat/runtime/media-types.ts new file mode 100644 index 000000000..1b169b3d6 --- /dev/null +++ b/compat/runtime/media-types.ts @@ -0,0 +1,20 @@ +// compat/runtime/media-types.ts +// Media type parsing - replacement for std/media_types/parse_media_type.ts + +export function parseMediaType( + contentType: string, +): [string, Record] { + const [mediaType, ...paramParts] = contentType.split(";").map((s) => s.trim()); + + const params: Record = {}; + for (const part of paramParts) { + const [key, value] = part.split("=").map((s) => s.trim()); + if (key && value) { + // Remove quotes if present + params[key] = value.replace(/^["']|["']$/g, ""); + } + } + + return [mediaType.toLowerCase(), params]; +} + diff --git a/compat/runtime/mod.ts b/compat/runtime/mod.ts new file mode 100644 index 000000000..cb33708a3 --- /dev/null +++ b/compat/runtime/mod.ts @@ -0,0 +1,23 @@ +// compat/runtime/mod.ts +// Runtime compatibility layer for apps +// Provides cross-runtime abstractions for Deno, Node.js, and Bun + +export * from "./env.ts"; +export * from "./inspect.ts"; +export * as colors from "./colors.ts"; +export * from "./async.ts"; +export { parseMediaType } from "./media-types.ts"; + +// Runtime detection +declare const Deno: unknown; +declare const Bun: unknown; + +export const isDeno = typeof Deno !== "undefined"; +export const isBun = typeof Bun !== "undefined" && !isDeno; +export const isNode = !isDeno && !isBun && typeof process !== "undefined"; +export const runtime: "deno" | "bun" | "node" = isDeno + ? "deno" + : isBun + ? "bun" + : "node"; + diff --git a/decohub/mod.ts b/decohub/mod.ts index 3f4a2c56d..1e13d4a85 100644 --- a/decohub/mod.ts +++ b/decohub/mod.ts @@ -1,3 +1,4 @@ +import { env } from "../compat/runtime/mod.ts"; import { Markdown } from "./components/Markdown.tsx"; import manifest, { Manifest } from "./manifest.gen.ts"; import { buildImportMap, ImportMap } from "@deco/deco/blocks"; @@ -15,7 +16,7 @@ export interface State { enableAdmin?: boolean; apps: DynamicApp[]; } -const DENY_DYNAMIC_IMPORT = Deno.env.get("DENY_DYNAMIC_IMPORT") === "true"; +const DENY_DYNAMIC_IMPORT = env.get("DENY_DYNAMIC_IMPORT") === "true"; /** * @title Deco Hub * @description Unlock apps and integrations on deco.cx diff --git a/records/utils.ts b/records/utils.ts index e709936fa..bce8f3199 100644 --- a/records/utils.ts +++ b/records/utils.ts @@ -1,7 +1,17 @@ +import { env } from "../compat/runtime/mod.ts"; import { Secret } from "../website/loaders/secret.ts"; import { brightGreen, brightRed } from "std/fmt/colors.ts"; import { join } from "https://deno.land/std@0.204.0/path/join.ts"; import { context } from "@deco/deco"; + +// Cross-runtime cwd +const cwd = (): string => { + if (typeof Deno !== "undefined") { + return (Deno as any).cwd(); + } + return process.cwd(); +}; + export interface StorageConfig { /** * @title Url @@ -13,13 +23,13 @@ export interface StorageConfig { */ authToken: Secret; } -export const getLocalDbFilename = () => join(Deno.cwd(), "sqlite.db"); +export const getLocalDbFilename = () => join(cwd(), "sqlite.db"); export const getLocalSQLClientConfig = () => ({ url: new URL(`file://${getLocalDbFilename()}`).href, authToken: "", }); export const getSQLClientConfig = ({ authToken, url }: StorageConfig) => { - const useProdDb = Deno.env.get("USE_PRODUCTION_DB"); + const useProdDb = env.get("USE_PRODUCTION_DB"); const useLocalDB = useProdDb !== undefined && useProdDb !== "1" || useProdDb === undefined && !context.isDeploy; if (useLocalDB) { diff --git a/typesense/utils/once.ts b/typesense/utils/once.ts index 79d76ea5b..66805ac78 100644 --- a/typesense/utils/once.ts +++ b/typesense/utils/once.ts @@ -1,4 +1,4 @@ -import { deferred } from "std/async/deferred.ts"; +import { deferred } from "../../compat/runtime/async.ts"; export const once = (cb: () => Promise) => { let promise = deferred(); diff --git a/utils/fetch.ts b/utils/fetch.ts index bacc59122..0fda11aaa 100644 --- a/utils/fetch.ts +++ b/utils/fetch.ts @@ -3,7 +3,7 @@ import { ExponentialBackoff, handleWhen, retry, -} from "https://esm.sh/cockatiel@3.1.1?target=es2019"; +} from "cockatiel"; import { HttpError } from "./http.ts"; import { fetch } from "@deco/deco"; // this error is thrown by deno deploy when the connection is closed by the server. diff --git a/utils/http.ts b/utils/http.ts index c1fa16dc6..a4d5474e4 100644 --- a/utils/http.ts +++ b/utils/http.ts @@ -1,8 +1,9 @@ import { type RequestInit } from "@deco/deco"; +import { env } from "../compat/runtime/mod.ts"; import { fetchSafe } from "./fetch.ts"; // Check if DEBUG_HTTP env var is set -const DEBUG_HTTP = Deno.env.get("DEBUG_HTTP") === "true"; +const DEBUG_HTTP = env.get("DEBUG_HTTP") === "true"; if (DEBUG_HTTP) { console.log("DEBUG_HTTP mode is:", DEBUG_HTTP ? "enabled" : "disabled"); } diff --git a/utils/pool.ts b/utils/pool.ts index 6845e5cbe..c86e2b570 100644 --- a/utils/pool.ts +++ b/utils/pool.ts @@ -1,4 +1,4 @@ -import { Deferred, deferred } from "std/async/deferred.ts"; +import { type Deferred, deferred } from "../compat/runtime/async.ts"; export const createPool = (resources: T[]) => { const taken = new Set(); diff --git a/utils/weakcache.ts b/utils/weakcache.ts index 59ba9e877..95e2f4310 100644 --- a/utils/weakcache.ts +++ b/utils/weakcache.ts @@ -1 +1 @@ -export * as weakcache from "npm:weak-lru-cache@1.0.0"; +export * as weakcache from "weak-lru-cache"; diff --git a/utils/worker.ts b/utils/worker.ts index 7b467182a..1fe8f0cab 100644 --- a/utils/worker.ts +++ b/utils/worker.ts @@ -1,5 +1,6 @@ // deno-lint-ignore-file no-explicit-any -import { Deferred, deferred } from "std/async/deferred.ts"; +import { deferred, type Deferred } from "../compat/runtime/async.ts"; +import { inspect } from "../compat/runtime/inspect.ts"; /** * Deco labs: 🐁🐁🐁 @@ -143,7 +144,7 @@ if (IS_WORKER) { } catch (error) { self.postMessage({ type: "invoke:reject", - payload: { id, reason: Deno.inspect(error) }, + payload: { id, reason: inspect(error) }, }); } @@ -160,7 +161,7 @@ if (IS_WORKER) { } catch (error) { self.postMessage({ type: "setup:reject", - payload: Deno.inspect(error), + payload: inspect(error), }); } diff --git a/vtex/compat/std/http/mod.ts b/vtex/compat/std/http/mod.ts new file mode 100644 index 000000000..8958d7613 --- /dev/null +++ b/vtex/compat/std/http/mod.ts @@ -0,0 +1,21 @@ +// Cross-runtime cookie utilities +export function getCookies(headers: Headers): Record { + const cookieHeader = headers.get("cookie"); + if (!cookieHeader) return {}; + + return Object.fromEntries( + cookieHeader.split(";").map((cookie) => { + const [name, ...valueParts] = cookie.trim().split("="); + return [name, valueParts.join("=")]; + }) + ); +} + +export function getSetCookies(headers: Headers): { name: string; value: string }[] { + const setCookies = headers.getSetCookie?.() ?? []; + return setCookies.map((cookie) => { + const [nameValue] = cookie.split(";"); + const [name, value] = nameValue.split("="); + return { name, value }; + }); +} diff --git a/vtex/utils/vtexId.ts b/vtex/utils/vtexId.ts index 51f367a94..5ca7725b9 100644 --- a/vtex/utils/vtexId.ts +++ b/vtex/utils/vtexId.ts @@ -1,5 +1,5 @@ -import { getCookies } from "std/http/mod.ts"; -import { decode } from "https://deno.land/x/djwt@v2.8/mod.ts"; +import { getCookies } from "../compat/std/http/mod.ts"; +import { decode } from "djwt"; import { stringify } from "./cookies.ts"; export const VTEX_ID_CLIENT_COOKIE = "VtexIdclientAutCookie"; diff --git a/website/components/Events.tsx b/website/components/Events.tsx index 837b980ed..4695f87fc 100644 --- a/website/components/Events.tsx +++ b/website/components/Events.tsx @@ -2,6 +2,7 @@ import { Head } from "$fresh/runtime.ts"; import { type AnalyticsEvent, type Deco } from "../../commerce/types.ts"; import { useScriptAsDataURI } from "@deco/deco/hooks"; import { DECO_SEGMENT, type Flag } from "@deco/deco"; +import { env } from "../../compat/runtime/mod.ts"; type EventHandler = (event?: AnalyticsEvent) => void | Promise; interface EventsAPI { dispatch: (event: unknown) => void; @@ -31,8 +32,8 @@ declare global { } } const ENABLE_IMAGE_OPTIMIZATION = - Deno.env.get("ENABLE_IMAGE_OPTIMIZATION") !== "false"; -const ENABLE_AZION_ASSETS = Deno.env.get("ENABLE_AZION_ASSETS") !== "false"; + env.get("ENABLE_IMAGE_OPTIMIZATION") !== "false"; +const ENABLE_AZION_ASSETS = env.get("ENABLE_AZION_ASSETS") !== "false"; /** * This function handles all ecommerce analytics events. * Add another ecommerce analytics modules here. diff --git a/website/components/Image.tsx b/website/components/Image.tsx index 087da9385..759977198 100644 --- a/website/components/Image.tsx +++ b/website/components/Image.tsx @@ -2,6 +2,7 @@ import { Head, IS_BROWSER } from "$fresh/runtime.ts"; import type { JSX } from "preact"; import { forwardRef } from "preact/compat"; import { Manifest } from "../manifest.gen.ts"; +import { env } from "../../compat/runtime/mod.ts"; export const PATH: `/live/invoke/${keyof Manifest["loaders"]}` = "/live/invoke/website/loaders/image.ts"; @@ -40,16 +41,16 @@ const isImageOptmizationEnabled = () => IS_BROWSER // deno-lint-ignore no-explicit-any ? (globalThis as any).DECO?.featureFlags?.enableImageOptimization - : Deno.env.get("ENABLE_IMAGE_OPTIMIZATION") !== "false"; + : env.get("ENABLE_IMAGE_OPTIMIZATION") !== "false"; const isAzionAssetsEnabled = () => IS_BROWSER // deno-lint-ignore no-explicit-any ? (globalThis as any).DECO?.featureFlags?.azionAssets - : Deno.env.get("ENABLE_AZION_ASSETS") !== "false"; + : env.get("ENABLE_AZION_ASSETS") !== "false"; const canShowWarning = () => - IS_BROWSER ? false : !Deno.env.get("DENO_DEPLOYMENT_ID"); + IS_BROWSER ? false : !env.get("DENO_DEPLOYMENT_ID"); interface OptimizationOptions { originalSrc: string; diff --git a/website/components/_Controls.tsx b/website/components/_Controls.tsx index ec7918795..bbc1d5a05 100644 --- a/website/components/_Controls.tsx +++ b/website/components/_Controls.tsx @@ -1,6 +1,9 @@ import { Head } from "$fresh/runtime.ts"; -import { DomInspectorActivators } from "https://deno.land/x/inspect_vscode@0.2.1/inspector.ts"; -import { DomInspector } from "https://deno.land/x/inspect_vscode@0.2.1/mod.ts"; +// DomInspector imports - use stub for non-Deno runtimes +import { + DomInspector, + DomInspectorActivators, +} from "@deco/deco"; import { Page } from "../../commerce/types.ts"; import { useScriptAsDataURI } from "@deco/deco/hooks"; import { context, type Flag, type Site } from "@deco/deco"; diff --git a/website/loaders/environment.ts b/website/loaders/environment.ts index b1796090c..b68a4acd3 100644 --- a/website/loaders/environment.ts +++ b/website/loaders/environment.ts @@ -1,3 +1,5 @@ +import { env } from "../../compat/runtime/mod.ts"; + /** * @title Environment * @hideOption true @@ -23,8 +25,8 @@ export interface Props { const getEnvironment = (props: Props): string | null => { const name = props?.name; - if (name && Deno.env.has(name)) { - return Deno.env.get(name)!; + if (name && env.has(name)) { + return env.get(name)!; } const value = props?.value; if (!value) { diff --git a/website/loaders/redirectsFromCsv.ts b/website/loaders/redirectsFromCsv.ts index 00e104cef..863000591 100644 --- a/website/loaders/redirectsFromCsv.ts +++ b/website/loaders/redirectsFromCsv.ts @@ -1,6 +1,23 @@ import { join } from "std/path/mod.ts"; import { Route } from "../flags/audience.ts"; +// Cross-runtime file reading +const readTextFile = async (path: string): Promise => { + if (typeof Deno !== "undefined") { + return (Deno as any).readTextFile(path); + } + const { readFile } = await import("node:fs/promises"); + return readFile(path, "utf-8"); +}; + +// Cross-runtime cwd +const cwd = (): string => { + if (typeof Deno !== "undefined") { + return (Deno as any).cwd(); + } + return process.cwd(); +}; + const REDIRECT_TYPE_ENUM = ["temporary", "permanent"]; const CONCATENATE_PARAMS_VALUES = ["true", "false"]; @@ -42,8 +59,8 @@ const getRedirectFromFile = async ( if (from.startsWith("http")) { redirectsRaw = await fetch(from).then((resp) => resp.text()); } else { - redirectsRaw = await Deno.readTextFile( - join(Deno.cwd(), join(...from.split("/"))), + redirectsRaw = await readTextFile( + join(cwd(), join(...from.split("/"))), ); } } catch (e) { diff --git a/website/loaders/secret.ts b/website/loaders/secret.ts index d21ecff11..437bb11c5 100644 --- a/website/loaders/secret.ts +++ b/website/loaders/secret.ts @@ -1,4 +1,4 @@ -import * as colors from "std/fmt/colors.ts"; +import { colors, env } from "../../compat/runtime/mod.ts"; import { once } from "../../typesense/utils/once.ts"; import { decryptFromHex, hasLocalCryptoKey } from "../utils/crypto.ts"; import { Context } from "@deco/deco"; @@ -36,8 +36,8 @@ const showWarningOnce = once(() => { }); const getSecret = async (props: Props): Promise => { const name = props?.name; - if (name && Deno.env.has(name)) { - return Promise.resolve(Deno.env.get(name)!); + if (name && env.has(name)) { + return Promise.resolve(env.get(name)!); } const encrypted = props?.encrypted; if (!encrypted) { diff --git a/website/matchers/cron.ts b/website/matchers/cron.ts index 7ba2fc870..8cb277bae 100644 --- a/website/matchers/cron.ts +++ b/website/matchers/cron.ts @@ -1,4 +1,4 @@ -import Cron from "https://deno.land/x/croner@6.0.3/dist/croner.js"; +import { Cron } from "croner"; /** * @titleBy cron diff --git a/website/matchers/environment.ts b/website/matchers/environment.ts index 7071e7acf..12efdf21a 100644 --- a/website/matchers/environment.ts +++ b/website/matchers/environment.ts @@ -1,3 +1,5 @@ +import { env } from "../../compat/runtime/mod.ts"; + /** * @title {{{environment}}} */ @@ -11,7 +13,8 @@ export interface Props { * @icon code */ const MatchEnvironment = ({ environment }: Props) => { - const deploymentId = Deno.env.get("DENO_DEPLOYMENT_ID") || ""; + // Check both Deno and Bun deployment indicators + const deploymentId = env.get("DENO_DEPLOYMENT_ID") || env.get("DECO_DEPLOYMENT_ID") || ""; return environment === "production" ? deploymentId !== "" diff --git a/website/utils/crypto.ts b/website/utils/crypto.ts index cb58b5652..9100faee3 100644 --- a/website/utils/crypto.ts +++ b/website/utils/crypto.ts @@ -1,7 +1,24 @@ /// -import { crypto } from "std/crypto/mod.ts"; -import { decode as decodeHex, encode as encodeHex } from "std/encoding/hex.ts"; +// Cross-runtime crypto using Web Crypto API +const crypto = globalThis.crypto; + +// Hex encode/decode utilities +const encodeHex = (data: Uint8Array): Uint8Array => { + const hex = Array.from(data) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + return new TextEncoder().encode(hex); +}; + +const decodeHex = (data: Uint8Array): Uint8Array => { + const hex = new TextDecoder().decode(data); + const bytes = new Uint8Array(hex.length / 2); + for (let i = 0; i < hex.length; i += 2) { + bytes[i / 2] = parseInt(hex.substr(i, 2), 16); + } + return bytes; +}; const generateKey = async (): Promise => { return await crypto.subtle.generateKey( @@ -44,15 +61,26 @@ export const fromSavedAESKey = async ( }; let key: null | Promise = null; -let kv: Deno.Kv | null = null; +// Runtime-agnostic KV and env access +declare const Deno: { + Kv: unknown; + openKv: () => Promise; + env: { has: (key: string) => boolean; get: (key: string) => string | undefined }; + args: string[]; +}; + +let kv: { get: (key: string[]) => Promise<{ value: T | null }>; atomic: () => { set: (key: string[], value: unknown) => { check: (v: unknown) => { commit: () => Promise<{ ok: boolean }> } } } } | null = null; try { - kv = await Deno?.openKv().catch((_err) => null); + if (typeof Deno !== "undefined" && Deno.openKv) { + kv = await Deno?.openKv().catch((_err: unknown) => null) as typeof kv; + } } catch { - console.warn("please run with `--unstable` to enable deno kv support"); + console.warn("Deno KV not available in this runtime"); } const cryptoKey = ["deco", "secret_cryptokey"]; -export const getSavedAES = (kv: Deno.Kv) => { +export const getSavedAES = (kv: typeof kv) => { + if (!kv) return Promise.resolve(null); return kv.get(cryptoKey).then(({ value }) => { return value; }); @@ -61,7 +89,10 @@ export const getSavedAES = (kv: Deno.Kv) => { export const CRYPTO_KEY_ENV_VAR = "DECO_CRYPTO_KEY"; export const hasLocalCryptoKey = () => { - return Deno.env.has(CRYPTO_KEY_ENV_VAR); + if (typeof Deno !== "undefined") { + return Deno.env.has(CRYPTO_KEY_ENV_VAR); + } + return typeof process !== "undefined" && !!process.env[CRYPTO_KEY_ENV_VAR]; }; export const generateAESKey = async (): Promise => { @@ -88,8 +119,11 @@ const getOrGenerateKey = (): Promise => { return key; } if (hasLocalCryptoKey()) { + const envValue = typeof Deno !== "undefined" + ? Deno.env.get(CRYPTO_KEY_ENV_VAR) + : process.env[CRYPTO_KEY_ENV_VAR]; const parsedAESKey: SavedAESKey = JSON.parse( - atob(Deno.env.get(CRYPTO_KEY_ENV_VAR)!), + atob(envValue!), ); return fromSavedAESKey({ key: new Uint8Array(Object.values(parsedAESKey.key)), @@ -154,7 +188,7 @@ export const decryptFromHex = async (encrypted: string) => { return { decrypted: textDecode(decryptedBytes) }; }; -if (import.meta.main) { +if (typeof Deno !== "undefined" && (import.meta as { main?: boolean }).main) { const [arg] = Deno.args; if (!arg) { // Generate and print a new CRYPTO_KEY diff --git a/website/utils/image/engines/deco/engine.ts b/website/utils/image/engines/deco/engine.ts index 149565cd5..26065b40e 100644 --- a/website/utils/image/engines/deco/engine.ts +++ b/website/utils/image/engines/deco/engine.ts @@ -1,6 +1,7 @@ +import { env } from "../../../../../compat/runtime/mod.ts"; import { createEngine } from "../remote/engine.ts"; -const ikid = Deno.env.get("DECO_IK_ID") ?? "decocx"; +const ikid = env.get("DECO_IK_ID") ?? "decocx"; export const engine = createEngine({ name: "deco", diff --git a/website/utils/image/engines/passThrough/engine.ts b/website/utils/image/engines/passThrough/engine.ts index a44c73a64..ea5bc6b44 100644 --- a/website/utils/image/engines/passThrough/engine.ts +++ b/website/utils/image/engines/passThrough/engine.ts @@ -1,9 +1,10 @@ +import { env } from "../../../../../compat/runtime/mod.ts"; import { createEngine as createRemoteEngine } from "../remote/engine.ts"; export const engine = createRemoteEngine({ name: "pass-through", accepts: () => - !Deno.env.has("IMAGES_ENGINE") || - Deno.env.get("IMAGES_ENGINE") === "pass-through", + !env.has("IMAGES_ENGINE") || + env.get("IMAGES_ENGINE") === "pass-through", urlFromParams: ({ src }) => new URL(src), }); diff --git a/website/utils/image/engines/wasm/engine.ts b/website/utils/image/engines/wasm/engine.ts index dee7ac221..82b15374c 100644 --- a/website/utils/image/engines/wasm/engine.ts +++ b/website/utils/image/engines/wasm/engine.ts @@ -1,4 +1,4 @@ -import { parseMediaType } from "std/media_types/parse_media_type.ts"; +import { inspect, parseMediaType } from "../../../../../compat/runtime/mod.ts"; import { HttpError } from "../../../../../utils/http.ts"; import { createPool } from "../../../../../utils/pool.ts"; import { createWorker } from "../../../../../utils/worker.ts"; @@ -59,7 +59,7 @@ const fetchImage = async ( const response = await fetch(src, init); if (!response.ok) { - throw new HttpError(response.status, Deno.inspect(response)); + throw new HttpError(response.status, inspect(response)); } const data = await response.arrayBuffer(); diff --git a/workflows/actions/start.ts b/workflows/actions/start.ts index e5ea323d6..ef48997f2 100644 --- a/workflows/actions/start.ts +++ b/workflows/actions/start.ts @@ -1,4 +1,5 @@ // deno-lint-ignore-file no-explicit-any +import { env } from "../../compat/runtime/mod.ts"; import { start } from "../initializer.ts"; // side-effect initialize import { toExecution, WorkflowExecution, WorkflowMetadata } from "../types.ts"; import { Workflow, WorkflowFn } from "@deco/deco/blocks"; @@ -92,7 +93,7 @@ export default async function startWorkflow< const { id, args, runtimeParameters } = props; const workflow = fromWorkflowProps(props); const context = Context.active(); - const service = Deno.env.get("MY_DURABLE_URL") ?? + const service = env.get("MY_DURABLE_URL") ?? (context.isDeploy ? `wss://deco-sites-${context.site}-${context.deploymentId}.deno.dev` : "ws://localhost:8000"); diff --git a/workflows/deps.ts b/workflows/deps.ts index 5f25ab76c..ba86361f4 100644 --- a/workflows/deps.ts +++ b/workflows/deps.ts @@ -1 +1,2 @@ -export * from "https://cdn.jsdelivr.net/gh/deco-cx/durable@0.5.3/sdk/deno/mod.ts"; +// Use deco's durable shim for runtime compatibility +export * from "@deco/durable"; diff --git a/workflows/initializer.ts b/workflows/initializer.ts index 30d315929..6752ce24e 100644 --- a/workflows/initializer.ts +++ b/workflows/initializer.ts @@ -1,3 +1,4 @@ +import { env } from "../compat/runtime/mod.ts"; import { cancel as durableCancel, get as durableGet, @@ -13,8 +14,8 @@ const LOCAL_OPTIONS = { token: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ1cm46ZGVjbzpzaXRlOjphZG1pbjpkZXBsb3ltZW50L3RzdCIsInN1YiI6InVybjpkZWNvOnNpdGU6Ong6ZGVwbG95bWVudC90c3QiLCJzY29wZXMiOlsiaHR0cDovL2xvY2FsaG9zdDo4MDAwLyoiLCJ3czovL2xvY2FsaG9zdDo4MDAwLyoiXX0.awdXDppwF-Dn7BwMWLz3hHqlx16HfVBuPuoGP4mVBihkMxwqDvZYWi_1Dg27u6ajg9br9qL6xSTlN8nauo89AyELaQavUIPDnW5u1yZpVZ5XE1C7DyVc3ncGe8L_PjuRqkfkc24POCiPVALYqKpJ7uERkjoSSRT5BBbuPvuWYZQaeNpkw6CUKWzod9myg7evtbIBEuLHnNyhT2hKmdzLuJNzakS-cyZVIQ6Pm_JDTQhdH15QyDNviJ6tM6HrNARgti40QUOAwRpACLZ16LsEpAitaZPBx7KNDr456indBP_HqZF6crO3yUQEFSN5Yb323VLjtaX2SVSqIP0uOLn0yA", }; -const decoDurableServiceUrl = Deno.env.get("DECO_DURABLE_SERVICE_URL"); -const durableToken = Deno.env.get("DURABLE_TOKEN"); +const decoDurableServiceUrl = env.get("DECO_DURABLE_SERVICE_URL"); +const durableToken = env.get("DURABLE_TOKEN"); const durableDefaultOpts = () => { const context = Context.active(); const remoteOptions = {