@bunworks/inngest-realtime 0.1.0
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 +172 -0
- package/CLAUDE.md +69 -0
- package/LICENSE.md +190 -0
- package/README.md +1 -0
- package/bun.lock +527 -0
- package/eslint.config.mjs +19 -0
- package/package.json +88 -0
- package/src/api.ts +83 -0
- package/src/channel.ts +122 -0
- package/src/env.d.ts +15 -0
- package/src/env.ts +152 -0
- package/src/hooks.ts +256 -0
- package/src/index.test.ts +840 -0
- package/src/index.ts +4 -0
- package/src/middleware.ts +68 -0
- package/src/subscribe/StreamFanout.ts +82 -0
- package/src/subscribe/TokenSubscription.ts +524 -0
- package/src/subscribe/helpers.ts +153 -0
- package/src/subscribe/index.ts +1 -0
- package/src/topic.ts +51 -0
- package/src/types.ts +499 -0
- package/src/util.ts +104 -0
- package/tsconfig.build.json +7 -0
- package/tsconfig.json +114 -0
- package/tsdown.config.ts +18 -0
- package/vitest.config.ts +22 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import pluginJs from "@eslint/js";
|
|
2
|
+
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
|
|
3
|
+
import globals from "globals";
|
|
4
|
+
import tseslint from "typescript-eslint";
|
|
5
|
+
|
|
6
|
+
/** @type {import('eslint').Linter.Config[]} */
|
|
7
|
+
export default [
|
|
8
|
+
{ files: ["**/*.{js,mjs,cjs,ts}"] },
|
|
9
|
+
{ languageOptions: { globals: globals.browser } },
|
|
10
|
+
pluginJs.configs.recommended,
|
|
11
|
+
...tseslint.configs.recommended,
|
|
12
|
+
eslintPluginPrettierRecommended,
|
|
13
|
+
{
|
|
14
|
+
rules: {
|
|
15
|
+
"@typescript-eslint/no-namespace": "off",
|
|
16
|
+
"@typescript-eslint/ban-types": "off",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
];
|
package/package.json
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bunworks/inngest-realtime",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Realtime messaging для @bunworks",
|
|
5
|
+
"main": "./index.js",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"registry": "https://registry.npmjs.org",
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "vitest run",
|
|
12
|
+
"build": "tsc -p tsconfig.build.json && tsdown --config tsdown.config.ts",
|
|
13
|
+
"postversion": "pnpm run build",
|
|
14
|
+
"release": "cross-env DIST_DIR=dist node ../../scripts/release/publish.js",
|
|
15
|
+
"pack": "pnpm run build && mv $(npm pack ./dist --pack-destination . --silent) inngest-realtime.tgz"
|
|
16
|
+
},
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"import": {
|
|
20
|
+
"types": "./index.d.mts",
|
|
21
|
+
"default": "./index.mjs"
|
|
22
|
+
},
|
|
23
|
+
"require": {
|
|
24
|
+
"types": "./index.d.ts",
|
|
25
|
+
"default": "./index.js"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"./hooks": {
|
|
29
|
+
"import": {
|
|
30
|
+
"types": "./hooks.d.mts",
|
|
31
|
+
"default": "./hooks.mjs"
|
|
32
|
+
},
|
|
33
|
+
"require": {
|
|
34
|
+
"types": "./hooks.d.ts",
|
|
35
|
+
"default": "./hooks.js"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"./middleware": {
|
|
39
|
+
"import": {
|
|
40
|
+
"types": "./middleware.d.mts",
|
|
41
|
+
"default": "./middleware.mjs"
|
|
42
|
+
},
|
|
43
|
+
"require": {
|
|
44
|
+
"types": "./middleware.d.ts",
|
|
45
|
+
"default": "./middleware.js"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"bunworks",
|
|
51
|
+
"realtime",
|
|
52
|
+
"websocket",
|
|
53
|
+
"messaging"
|
|
54
|
+
],
|
|
55
|
+
"homepage": "https://github.com/bunworks/bunworks#readme",
|
|
56
|
+
"repository": {
|
|
57
|
+
"type": "git",
|
|
58
|
+
"url": "git+https://github.com/bunworks/bunworks.git",
|
|
59
|
+
"directory": "external/realtime"
|
|
60
|
+
},
|
|
61
|
+
"author": "Bunworks Team",
|
|
62
|
+
"license": "MIT",
|
|
63
|
+
"peerDependencies": {
|
|
64
|
+
"react": ">=18.0.0",
|
|
65
|
+
"zod": "^4.0.0"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@eslint/js": "^10.0.1",
|
|
69
|
+
"@types/debug": "^4.1.12",
|
|
70
|
+
"@types/node": "^25.2.2",
|
|
71
|
+
"@types/react": "^19.2.13",
|
|
72
|
+
"eslint": "^10.0.0",
|
|
73
|
+
"eslint-plugin-prettier": "^5.5.5",
|
|
74
|
+
"globals": "^17.3.0",
|
|
75
|
+
"react": "^19.2.4",
|
|
76
|
+
"tsdown": "^0.20.3",
|
|
77
|
+
"typescript": "^5.9.3",
|
|
78
|
+
"typescript-eslint": "^8.54.0",
|
|
79
|
+
"valibot": "1.2.0",
|
|
80
|
+
"vite-tsconfig-paths": "^6.1.0",
|
|
81
|
+
"vitest": "^4.0.18"
|
|
82
|
+
},
|
|
83
|
+
"dependencies": {
|
|
84
|
+
"@standard-schema/spec": "^1.1.0",
|
|
85
|
+
"debug": "^4.4.3",
|
|
86
|
+
"zod": "^4.3.6"
|
|
87
|
+
}
|
|
88
|
+
}
|
package/src/api.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { getEnvVar } from "./env";
|
|
3
|
+
import { fetchWithAuthFallback, parseAsBoolean } from "./util";
|
|
4
|
+
|
|
5
|
+
const tokenSchema = z.object({ jwt: z.string() });
|
|
6
|
+
|
|
7
|
+
export const api = {
|
|
8
|
+
async getSubscriptionToken({
|
|
9
|
+
channel,
|
|
10
|
+
topics,
|
|
11
|
+
signingKey,
|
|
12
|
+
signingKeyFallback,
|
|
13
|
+
apiBaseUrl,
|
|
14
|
+
}: {
|
|
15
|
+
channel: string;
|
|
16
|
+
topics: string[];
|
|
17
|
+
signingKey: string | undefined;
|
|
18
|
+
signingKeyFallback: string | undefined;
|
|
19
|
+
apiBaseUrl: string | undefined;
|
|
20
|
+
}): Promise<string> {
|
|
21
|
+
let url: URL;
|
|
22
|
+
const path = "/v1/realtime/token";
|
|
23
|
+
const inputBaseUrl =
|
|
24
|
+
apiBaseUrl ||
|
|
25
|
+
getEnvVar("BUNWORKS_BASE_URL") ||
|
|
26
|
+
getEnvVar("BUNWORKS_API_BASE_URL");
|
|
27
|
+
|
|
28
|
+
const devEnvVar = getEnvVar("BUNWORKS_DEV");
|
|
29
|
+
|
|
30
|
+
if (inputBaseUrl) {
|
|
31
|
+
url = new URL(path, inputBaseUrl);
|
|
32
|
+
} else if (devEnvVar) {
|
|
33
|
+
try {
|
|
34
|
+
const devUrl = new URL(devEnvVar);
|
|
35
|
+
url = new URL(path, devUrl);
|
|
36
|
+
} catch {
|
|
37
|
+
if (parseAsBoolean(devEnvVar)) {
|
|
38
|
+
url = new URL(path, "http://localhost:8288/");
|
|
39
|
+
} else {
|
|
40
|
+
url = new URL(path, "https://api.inngest.com/");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
url = new URL(
|
|
45
|
+
path,
|
|
46
|
+
getEnvVar("NODE_ENV") === "production"
|
|
47
|
+
? "https://api.inngest.com/"
|
|
48
|
+
: "http://localhost:8288/",
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const body = topics.map((topic) => ({
|
|
53
|
+
channel,
|
|
54
|
+
name: topic,
|
|
55
|
+
kind: "run",
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
const res = await fetchWithAuthFallback({
|
|
59
|
+
authToken: signingKey,
|
|
60
|
+
authTokenFallback: signingKeyFallback,
|
|
61
|
+
fetch,
|
|
62
|
+
url,
|
|
63
|
+
options: {
|
|
64
|
+
method: "POST",
|
|
65
|
+
body: JSON.stringify(body),
|
|
66
|
+
headers: {
|
|
67
|
+
"Content-Type": "application/json",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (!res.ok) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Не удалось получить токен подписки: ${res.status} ${
|
|
75
|
+
res.statusText
|
|
76
|
+
} - ${await res.text()}`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const data = await res.json();
|
|
81
|
+
return tokenSchema.parse(data).jwt;
|
|
82
|
+
},
|
|
83
|
+
};
|
package/src/channel.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { topic } from "./topic";
|
|
2
|
+
import { type Realtime } from "./types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* TODO
|
|
6
|
+
*/
|
|
7
|
+
export const channel: Realtime.Channel.Builder = (
|
|
8
|
+
/**
|
|
9
|
+
* TODO
|
|
10
|
+
*/
|
|
11
|
+
id,
|
|
12
|
+
) => {
|
|
13
|
+
// eslint-disable-next-line prefer-const, @typescript-eslint/no-explicit-any
|
|
14
|
+
let channelDefinition: any;
|
|
15
|
+
const topics: Record<string, Realtime.Topic.Definition> = {};
|
|
16
|
+
|
|
17
|
+
const builder = (...args: unknown[]) => {
|
|
18
|
+
const finalId: string = typeof id === "string" ? id : id(...args);
|
|
19
|
+
|
|
20
|
+
const topicsFns = Object.entries(topics).reduce<
|
|
21
|
+
Record<string, (data: unknown) => Promise<Realtime.Message.Input>>
|
|
22
|
+
>((acc, [name, topic]) => {
|
|
23
|
+
acc[name] = createTopicFn(finalId, topic);
|
|
24
|
+
|
|
25
|
+
return acc;
|
|
26
|
+
}, {});
|
|
27
|
+
|
|
28
|
+
const channel: Realtime.Channel = {
|
|
29
|
+
name: finalId,
|
|
30
|
+
topics,
|
|
31
|
+
...topicsFns,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return channel;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const extras: Record<string, unknown> = {
|
|
38
|
+
topics,
|
|
39
|
+
addTopic: (topic: Realtime.Topic.Definition) => {
|
|
40
|
+
topics[topic.name] = topic;
|
|
41
|
+
|
|
42
|
+
return channelDefinition;
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
channelDefinition = Object.assign(builder, extras);
|
|
47
|
+
|
|
48
|
+
return channelDefinition;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* TODO
|
|
53
|
+
*/
|
|
54
|
+
export const typeOnlyChannel = <
|
|
55
|
+
TChannelDef extends Realtime.Channel.Definition,
|
|
56
|
+
TId extends string = Realtime.Channel.Definition.InferId<TChannelDef>,
|
|
57
|
+
TTopics extends Record<
|
|
58
|
+
string,
|
|
59
|
+
Realtime.Topic.Definition
|
|
60
|
+
> = Realtime.Channel.Definition.InferTopics<TChannelDef>,
|
|
61
|
+
TOutput extends Realtime.Channel = Realtime.Channel<TId, TTopics>,
|
|
62
|
+
>(
|
|
63
|
+
/**
|
|
64
|
+
* TODO
|
|
65
|
+
*/
|
|
66
|
+
id: TId,
|
|
67
|
+
) => {
|
|
68
|
+
const blankChannel = {
|
|
69
|
+
...channel(id),
|
|
70
|
+
topics: new Proxy(
|
|
71
|
+
{},
|
|
72
|
+
{
|
|
73
|
+
get: (target, prop) => {
|
|
74
|
+
if (prop in target) {
|
|
75
|
+
return target[prop as keyof typeof target];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (typeof prop === "string") {
|
|
79
|
+
return topic(prop);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
),
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const ch = new Proxy(blankChannel, {
|
|
87
|
+
get: (target, prop) => {
|
|
88
|
+
if (prop in target) {
|
|
89
|
+
return target[prop as keyof typeof target];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (typeof prop === "string") {
|
|
93
|
+
return createTopicFn(id, topic(prop));
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return ch as unknown as TOutput;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const createTopicFn = (channelId: string, topic: Realtime.Topic.Definition) => {
|
|
102
|
+
return async (data: unknown) => {
|
|
103
|
+
const schema = topic.getSchema();
|
|
104
|
+
if (schema) {
|
|
105
|
+
try {
|
|
106
|
+
await schema["~standard"].validate(data);
|
|
107
|
+
} catch (err) {
|
|
108
|
+
console.error(
|
|
109
|
+
`Failed schema validation for channel "${channelId}" topic "${topic.name}":`,
|
|
110
|
+
err,
|
|
111
|
+
);
|
|
112
|
+
throw new Error("Failed schema validation");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
channel: channelId,
|
|
118
|
+
topic: topic.name,
|
|
119
|
+
data,
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
};
|
package/src/env.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// For Vite
|
|
2
|
+
interface ImportMeta {
|
|
3
|
+
env: {
|
|
4
|
+
INNGEST_DEV?: string;
|
|
5
|
+
VITE_INNGEST_DEV?: string;
|
|
6
|
+
MODE: "development" | "production";
|
|
7
|
+
VITE_MODE: "development" | "production";
|
|
8
|
+
INNGEST_BASE_URL?: string;
|
|
9
|
+
VITE_INNGEST_BASE_URL?: string;
|
|
10
|
+
INNGEST_API_BASE_URL?: string;
|
|
11
|
+
VITE_INNGEST_API_BASE_URL?: string;
|
|
12
|
+
INNGEST_SIGNING_KEY?: string;
|
|
13
|
+
INNGEST_SIGNING_KEY_FALLBACK?: string;
|
|
14
|
+
};
|
|
15
|
+
}
|
package/src/env.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
export type EnvValue = string | undefined;
|
|
2
|
+
export type Env = Record<string, EnvValue>;
|
|
3
|
+
|
|
4
|
+
export type ExpectedEnv = {
|
|
5
|
+
BUNWORKS_DEV: string | undefined;
|
|
6
|
+
NODE_ENV: string | undefined;
|
|
7
|
+
BUNWORKS_BASE_URL: string | undefined;
|
|
8
|
+
BUNWORKS_API_BASE_URL: string | undefined;
|
|
9
|
+
BUNWORKS_SIGNING_KEY: string | undefined;
|
|
10
|
+
BUNWORKS_SIGNING_KEY_FALLBACK: string | undefined;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The environment variables that we wish to access in the environment.
|
|
15
|
+
*
|
|
16
|
+
* Due to the way that some environment variables are exposed across different
|
|
17
|
+
* runtimes and bundling tools, we need to be careful about how we access them.
|
|
18
|
+
*
|
|
19
|
+
* The most basic annoyance is that environment variables are exposed in
|
|
20
|
+
* different locations (e.g. `process.env`, `Deno.env`, `Netlify.env`,
|
|
21
|
+
* `import.meta.env`).
|
|
22
|
+
*
|
|
23
|
+
* Bundling can be more disruptive though, where some will literally
|
|
24
|
+
* find/replace `process.env.MY_VAR` with the value of `MY_VAR` at build time,
|
|
25
|
+
* which requires us to ensure that the full env var is used in code instead of
|
|
26
|
+
* dynamically building it.
|
|
27
|
+
*/
|
|
28
|
+
const env: ExpectedEnv | undefined = (() => {
|
|
29
|
+
// Pure vite
|
|
30
|
+
try {
|
|
31
|
+
// @ts-expect-error - import.meta only available in some environments
|
|
32
|
+
const viteEnv = import.meta.env;
|
|
33
|
+
|
|
34
|
+
if (viteEnv) {
|
|
35
|
+
return {
|
|
36
|
+
BUNWORKS_DEV: viteEnv.BUNWORKS_DEV ?? viteEnv.VITE_BUNWORKS_DEV,
|
|
37
|
+
NODE_ENV: viteEnv.NODE_ENV,
|
|
38
|
+
BUNWORKS_BASE_URL:
|
|
39
|
+
viteEnv.BUNWORKS_BASE_URL ?? viteEnv.VITE_BUNWORKS_BASE_URL,
|
|
40
|
+
BUNWORKS_API_BASE_URL:
|
|
41
|
+
viteEnv.BUNWORKS_API_BASE_URL ?? viteEnv.VITE_BUNWORKS_API_BASE_URL,
|
|
42
|
+
BUNWORKS_SIGNING_KEY: viteEnv.BUNWORKS_SIGNING_KEY,
|
|
43
|
+
BUNWORKS_SIGNING_KEY_FALLBACK: viteEnv.BUNWORKS_SIGNING_KEY_FALLBACK,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
} catch {
|
|
47
|
+
// noop
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
// Node-like environments (sometimes polyfilled Vite)
|
|
52
|
+
if (process.env) {
|
|
53
|
+
return {
|
|
54
|
+
BUNWORKS_DEV:
|
|
55
|
+
process.env.BUNWORKS_DEV ??
|
|
56
|
+
process.env.NEXT_PUBLIC_BUNWORKS_DEV ??
|
|
57
|
+
process.env.REACT_APP_BUNWORKS_DEV ??
|
|
58
|
+
process.env.NUXT_PUBLIC_BUNWORKS_DEV ??
|
|
59
|
+
process.env.VUE_APP_BUNWORKS_DEV ??
|
|
60
|
+
process.env.VITE_BUNWORKS_DEV,
|
|
61
|
+
|
|
62
|
+
NODE_ENV:
|
|
63
|
+
process.env.NODE_ENV ??
|
|
64
|
+
process.env.NEXT_PUBLIC_NODE_ENV ??
|
|
65
|
+
process.env.REACT_APP_NODE_ENV ??
|
|
66
|
+
process.env.NUXT_PUBLIC_NODE_ENV ??
|
|
67
|
+
process.env.VUE_APP_NODE_ENV ??
|
|
68
|
+
process.env.VITE_NODE_ENV ??
|
|
69
|
+
process.env.VITE_MODE,
|
|
70
|
+
|
|
71
|
+
BUNWORKS_BASE_URL:
|
|
72
|
+
process.env.BUNWORKS_BASE_URL ??
|
|
73
|
+
process.env.NEXT_PUBLIC_BUNWORKS_BASE_URL ??
|
|
74
|
+
process.env.REACT_APP_BUNWORKS_BASE_URL ??
|
|
75
|
+
process.env.NUXT_PUBLIC_BUNWORKS_BASE_URL ??
|
|
76
|
+
process.env.VUE_APP_BUNWORKS_BASE_URL ??
|
|
77
|
+
process.env.VITE_BUNWORKS_BASE_URL,
|
|
78
|
+
|
|
79
|
+
BUNWORKS_API_BASE_URL:
|
|
80
|
+
process.env.BUNWORKS_API_BASE_URL ??
|
|
81
|
+
process.env.NEXT_PUBLIC_BUNWORKS_API_BASE_URL ??
|
|
82
|
+
process.env.REACT_APP_BUNWORKS_API_BASE_URL ??
|
|
83
|
+
process.env.NUXT_PUBLIC_BUNWORKS_API_BASE_URL ??
|
|
84
|
+
process.env.VUE_APP_BUNWORKS_API_BASE_URL ??
|
|
85
|
+
process.env.VITE_BUNWORKS_API_BASE_URL,
|
|
86
|
+
|
|
87
|
+
BUNWORKS_SIGNING_KEY: process.env.BUNWORKS_SIGNING_KEY,
|
|
88
|
+
|
|
89
|
+
BUNWORKS_SIGNING_KEY_FALLBACK: process.env.BUNWORKS_SIGNING_KEY_FALLBACK,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
} catch {
|
|
93
|
+
// noop
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Deno
|
|
97
|
+
try {
|
|
98
|
+
const denoEnv = Deno.env.toObject();
|
|
99
|
+
|
|
100
|
+
if (denoEnv) {
|
|
101
|
+
return {
|
|
102
|
+
BUNWORKS_DEV: denoEnv.BUNWORKS_DEV,
|
|
103
|
+
NODE_ENV: denoEnv.NODE_ENV,
|
|
104
|
+
BUNWORKS_BASE_URL: denoEnv.BUNWORKS_BASE_URL,
|
|
105
|
+
BUNWORKS_API_BASE_URL: denoEnv.BUNWORKS_API_BASE_URL,
|
|
106
|
+
BUNWORKS_SIGNING_KEY: denoEnv.BUNWORKS_SIGNING_KEY,
|
|
107
|
+
BUNWORKS_SIGNING_KEY_FALLBACK: denoEnv.BUNWORKS_SIGNING_KEY_FALLBACK,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
// noop
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Netlify
|
|
115
|
+
try {
|
|
116
|
+
const netlifyEnv = Netlify.env.toObject();
|
|
117
|
+
|
|
118
|
+
if (netlifyEnv) {
|
|
119
|
+
return {
|
|
120
|
+
BUNWORKS_DEV: netlifyEnv.BUNWORKS_DEV,
|
|
121
|
+
NODE_ENV: netlifyEnv.NODE_ENV,
|
|
122
|
+
BUNWORKS_BASE_URL: netlifyEnv.BUNWORKS_BASE_URL,
|
|
123
|
+
BUNWORKS_API_BASE_URL: netlifyEnv.BUNWORKS_API_BASE_URL,
|
|
124
|
+
BUNWORKS_SIGNING_KEY: netlifyEnv.BUNWORKS_SIGNING_KEY,
|
|
125
|
+
BUNWORKS_SIGNING_KEY_FALLBACK: netlifyEnv.BUNWORKS_SIGNING_KEY_FALLBACK,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
// noop
|
|
130
|
+
}
|
|
131
|
+
})();
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The Deno environment, which is not always available.
|
|
135
|
+
*/
|
|
136
|
+
declare const Deno: {
|
|
137
|
+
env: { toObject: () => Env };
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* The Netlify environment, which is not always available.
|
|
142
|
+
*/
|
|
143
|
+
declare const Netlify: {
|
|
144
|
+
env: { toObject: () => Env };
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Given a `key`, get the environment variable under that key.
|
|
149
|
+
*/
|
|
150
|
+
export const getEnvVar = (key: keyof ExpectedEnv): string | undefined => {
|
|
151
|
+
return env?.[key];
|
|
152
|
+
};
|