@bunizao/contracts 0.24.0 → 0.26.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/README.md +6 -0
- package/dist/runtime/env.d.ts +31 -0
- package/dist/runtime/json-response.d.ts +5 -0
- package/dist/runtime/query.d.ts +5 -0
- package/dist/runtime.d.ts +7 -0
- package/dist/runtime.js +138 -0
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -16,5 +16,11 @@ npm install @bunizao/contracts@0.1.0
|
|
|
16
16
|
The package exports the complete contract from `@bunizao/contracts` and the
|
|
17
17
|
feature modules from `@bunizao/contracts/{analytics,admin,content,listening,mood,notify,routes,telegram-ops}`.
|
|
18
18
|
|
|
19
|
+
`@bunizao/contracts/runtime` is the one subpath that ships code rather than
|
|
20
|
+
types: the env readers (`createEnvReaders`, `readRuntimeValue`,
|
|
21
|
+
`setRuntimeEnvSource`), the JSON response helpers and the query parsers both
|
|
22
|
+
Workers run. Bind the env readers to the bundle's own `import.meta.env` once,
|
|
23
|
+
in a local module, since bundlers only replace it in application source.
|
|
24
|
+
|
|
19
25
|
The package is source-available but not licensed for redistribution. See
|
|
20
26
|
[`LICENSE`](LICENSE) for the applicable terms.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime environment readers shared by the public site and the API Worker.
|
|
3
|
+
*
|
|
4
|
+
* A value is looked up in this order: `process.env` (local shells and tests),
|
|
5
|
+
* the build-time env the consumer binds through `createEnvReaders`, then the
|
|
6
|
+
* request-time Worker env: `locals.env`, `locals.runtime.env`, and finally the
|
|
7
|
+
* env a Worker entrypoint registered with `setRuntimeEnvSource` for code that
|
|
8
|
+
* runs without request locals (queues, crons, webhooks).
|
|
9
|
+
*
|
|
10
|
+
* This module never touches `import.meta.env` itself: bundlers only replace it
|
|
11
|
+
* in application source, so each consumer passes its own build env in.
|
|
12
|
+
*/
|
|
13
|
+
export type EnvSource = Record<string, unknown>;
|
|
14
|
+
export interface RuntimeEnvLocals {
|
|
15
|
+
runtime?: {
|
|
16
|
+
env?: EnvSource;
|
|
17
|
+
};
|
|
18
|
+
env?: EnvSource;
|
|
19
|
+
}
|
|
20
|
+
export declare function setRuntimeEnvSource(env: EnvSource): void;
|
|
21
|
+
export declare function readRuntimeEnvSource(locals: unknown): EnvSource | undefined;
|
|
22
|
+
export declare function readRuntimeValue(locals: unknown, name: string): string | undefined;
|
|
23
|
+
export interface EnvReaders {
|
|
24
|
+
readOptionalEnv(locals: unknown, name: string, buildEnv?: EnvSource): string | undefined;
|
|
25
|
+
readEnv(locals: unknown, name: string, buildEnv?: EnvSource): string;
|
|
26
|
+
readPublicEnv(locals: unknown, name: string, buildEnv?: EnvSource): string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Binds the readers to a consumer's build-time env, usually `import.meta.env`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createEnvReaders(defaultBuildEnv: EnvSource): EnvReaders;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function json(status: number, data: unknown, headers?: HeadersInit): Response;
|
|
2
|
+
export declare function jsonOk(data: unknown, headers?: HeadersInit): Response;
|
|
3
|
+
export declare function jsonError(status: number, error: string, headers?: HeadersInit, extras?: Record<string, unknown>): Response;
|
|
4
|
+
export declare function jsonBadRequest(error: string, headers?: HeadersInit, extras?: Record<string, unknown>): Response;
|
|
5
|
+
export declare function jsonTooManyRequests(headers?: HeadersInit): Response;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function readCursorQuery(url: URL, name: string): string;
|
|
2
|
+
export declare function isValidCursor(value: string): boolean;
|
|
3
|
+
export declare function readIntQuery(url: URL, name: string): number | null;
|
|
4
|
+
export declare function readEnumQuery<T extends string>(url: URL, name: string, allowedValues: readonly T[], defaultValue: T): T;
|
|
5
|
+
export declare function readBooleanFlag(url: URL, name: string, defaultValue?: boolean): boolean;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime helpers both Workers execute: env lookup, JSON responses and query
|
|
3
|
+
* parsing. Everything else in this package is types and constants.
|
|
4
|
+
*/
|
|
5
|
+
export { createEnvReaders, readRuntimeEnvSource, readRuntimeValue, setRuntimeEnvSource, type EnvReaders, type EnvSource, type RuntimeEnvLocals, } from './runtime/env';
|
|
6
|
+
export { json, jsonBadRequest, jsonError, jsonOk, jsonTooManyRequests, } from './runtime/json-response';
|
|
7
|
+
export { isValidCursor, readBooleanFlag, readCursorQuery, readEnumQuery, readIntQuery, } from './runtime/query';
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// src/runtime/env.ts
|
|
2
|
+
function record(value) {
|
|
3
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
4
|
+
}
|
|
5
|
+
function readValue(source, name) {
|
|
6
|
+
const raw = source?.[name];
|
|
7
|
+
if (typeof raw !== "string") {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const value = raw.trim();
|
|
11
|
+
return value ? value : undefined;
|
|
12
|
+
}
|
|
13
|
+
function readWorkerEnv() {
|
|
14
|
+
return record(globalThis.__BUXX_RUNTIME_ENV__);
|
|
15
|
+
}
|
|
16
|
+
function setRuntimeEnvSource(env) {
|
|
17
|
+
globalThis.__BUXX_RUNTIME_ENV__ = env;
|
|
18
|
+
}
|
|
19
|
+
function readRuntimeEnvSource(locals) {
|
|
20
|
+
const localRecord = record(locals);
|
|
21
|
+
const directEnv = record(localRecord.env);
|
|
22
|
+
if (Object.keys(directEnv).length > 0) {
|
|
23
|
+
return directEnv;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const runtimeEnv = record(record(localRecord.runtime).env);
|
|
27
|
+
if (Object.keys(runtimeEnv).length > 0) {
|
|
28
|
+
return runtimeEnv;
|
|
29
|
+
}
|
|
30
|
+
} catch {}
|
|
31
|
+
const workerEnv = readWorkerEnv();
|
|
32
|
+
return Object.keys(workerEnv).length > 0 ? workerEnv : undefined;
|
|
33
|
+
}
|
|
34
|
+
function readRuntimeValue(locals, name) {
|
|
35
|
+
const localRecord = record(locals);
|
|
36
|
+
const directValue = readValue(record(localRecord.env), name);
|
|
37
|
+
if (directValue) {
|
|
38
|
+
return directValue;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const runtimeValue = readValue(record(record(localRecord.runtime).env), name);
|
|
42
|
+
if (runtimeValue) {
|
|
43
|
+
return runtimeValue;
|
|
44
|
+
}
|
|
45
|
+
} catch {}
|
|
46
|
+
return readValue(readWorkerEnv(), name);
|
|
47
|
+
}
|
|
48
|
+
function readProcessEnv(name) {
|
|
49
|
+
const processEnv = globalThis.process?.env;
|
|
50
|
+
return readValue(processEnv, name);
|
|
51
|
+
}
|
|
52
|
+
function createEnvReaders(defaultBuildEnv) {
|
|
53
|
+
const readOptionalEnv = (locals, name, buildEnv = defaultBuildEnv) => {
|
|
54
|
+
return readProcessEnv(name) ?? readValue(buildEnv, name) ?? readRuntimeValue(locals, name);
|
|
55
|
+
};
|
|
56
|
+
const readEnv = (locals, name, buildEnv = defaultBuildEnv) => {
|
|
57
|
+
return readOptionalEnv(locals, name, buildEnv) ?? "";
|
|
58
|
+
};
|
|
59
|
+
const readPublicEnv = (locals, name, buildEnv = defaultBuildEnv) => {
|
|
60
|
+
const publicName = name.startsWith("PUBLIC_") ? name : `PUBLIC_${name}`;
|
|
61
|
+
return readEnv(locals, publicName, buildEnv);
|
|
62
|
+
};
|
|
63
|
+
return { readOptionalEnv, readEnv, readPublicEnv };
|
|
64
|
+
}
|
|
65
|
+
// src/runtime/json-response.ts
|
|
66
|
+
function createJsonHeaders(headers) {
|
|
67
|
+
const next = new Headers(headers);
|
|
68
|
+
if (!next.has("Content-Type")) {
|
|
69
|
+
next.set("Content-Type", "application/json");
|
|
70
|
+
}
|
|
71
|
+
return next;
|
|
72
|
+
}
|
|
73
|
+
function json(status, data, headers) {
|
|
74
|
+
return new Response(JSON.stringify(data), {
|
|
75
|
+
status,
|
|
76
|
+
headers: createJsonHeaders(headers)
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function jsonOk(data, headers) {
|
|
80
|
+
return json(200, data, headers);
|
|
81
|
+
}
|
|
82
|
+
function jsonError(status, error, headers, extras) {
|
|
83
|
+
return json(status, { error, ...extras ?? {} }, headers);
|
|
84
|
+
}
|
|
85
|
+
function jsonBadRequest(error, headers, extras) {
|
|
86
|
+
return jsonError(400, error, headers, extras);
|
|
87
|
+
}
|
|
88
|
+
function jsonTooManyRequests(headers) {
|
|
89
|
+
return jsonError(429, "Too Many Requests", headers);
|
|
90
|
+
}
|
|
91
|
+
// src/runtime/query.ts
|
|
92
|
+
var CURSOR_PATTERN = /^\d{1,20}$/;
|
|
93
|
+
var INTEGER_PATTERN = /^-?\d+$/;
|
|
94
|
+
function readCursorQuery(url, name) {
|
|
95
|
+
return (url.searchParams.get(name) ?? "").trim();
|
|
96
|
+
}
|
|
97
|
+
function isValidCursor(value) {
|
|
98
|
+
return value === "" || CURSOR_PATTERN.test(value);
|
|
99
|
+
}
|
|
100
|
+
function readIntQuery(url, name) {
|
|
101
|
+
const value = (url.searchParams.get(name) ?? "").trim();
|
|
102
|
+
if (!value || !INTEGER_PATTERN.test(value)) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const parsed = Number.parseInt(value, 10);
|
|
106
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
107
|
+
}
|
|
108
|
+
function readEnumQuery(url, name, allowedValues, defaultValue) {
|
|
109
|
+
const value = (url.searchParams.get(name) ?? "").trim();
|
|
110
|
+
return allowedValues.includes(value) ? value : defaultValue;
|
|
111
|
+
}
|
|
112
|
+
function readBooleanFlag(url, name, defaultValue = false) {
|
|
113
|
+
const value = url.searchParams.get(name);
|
|
114
|
+
if (value === null) {
|
|
115
|
+
return defaultValue;
|
|
116
|
+
}
|
|
117
|
+
const normalized = value.trim().toLowerCase();
|
|
118
|
+
if (!normalized) {
|
|
119
|
+
return defaultValue;
|
|
120
|
+
}
|
|
121
|
+
return normalized !== "0" && normalized !== "false" && normalized !== "no" && normalized !== "off";
|
|
122
|
+
}
|
|
123
|
+
export {
|
|
124
|
+
createEnvReaders,
|
|
125
|
+
isValidCursor,
|
|
126
|
+
json,
|
|
127
|
+
jsonBadRequest,
|
|
128
|
+
jsonError,
|
|
129
|
+
jsonOk,
|
|
130
|
+
jsonTooManyRequests,
|
|
131
|
+
readBooleanFlag,
|
|
132
|
+
readCursorQuery,
|
|
133
|
+
readEnumQuery,
|
|
134
|
+
readIntQuery,
|
|
135
|
+
readRuntimeEnvSource,
|
|
136
|
+
readRuntimeValue,
|
|
137
|
+
setRuntimeEnvSource
|
|
138
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bunizao/contracts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "Shared TypeScript contracts for buxx.me services and clients.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -83,6 +83,11 @@
|
|
|
83
83
|
"import": "./dist/routes.js",
|
|
84
84
|
"default": "./dist/routes.js"
|
|
85
85
|
},
|
|
86
|
+
"./runtime": {
|
|
87
|
+
"types": "./dist/runtime.d.ts",
|
|
88
|
+
"import": "./dist/runtime.js",
|
|
89
|
+
"default": "./dist/runtime.js"
|
|
90
|
+
},
|
|
86
91
|
"./telegram-ops": {
|
|
87
92
|
"types": "./dist/telegram-ops.d.ts",
|
|
88
93
|
"import": "./dist/telegram-ops.js",
|