@terrafirmagreg-team/data 0.0.0-stage → 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/package.json CHANGED
@@ -1,6 +1,38 @@
1
1
  {
2
2
  "name": "@terrafirmagreg-team/data",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": "./src/lib/index.ts",
7
+ "./*": [
8
+ "./src/*.ts",
9
+ "./src/*.tsx",
10
+ "./src/*/index.ts",
11
+ "./src/*/index.tsx",
12
+ "./src/*/*.ts",
13
+ "./src/*/*.tsx",
14
+ "./src/*/*/index.ts",
15
+ "./src/*/*/index.tsx"
16
+ ]
17
+ },
18
+ "files": [
19
+ "src",
20
+ "styles",
21
+ "styles.css",
22
+ "locales"
23
+ ],
24
+ "dependencies": {
25
+ "server-only": "^0.0.1"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^26.6.5"
29
+ },
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/TerraFirmaGreg-Team/Web-Foundation.git",
33
+ "directory": "packages/data"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ }
6
38
  }
@@ -0,0 +1,50 @@
1
+ import 'server-only';
2
+ import { createCipheriv, createDecipheriv, createHash, randomBytes } from 'node:crypto';
3
+
4
+ const key = (secret: string) => createHash('sha256').update(secret).digest();
5
+
6
+ export function encrypt(plaintext: string, secret: string): string {
7
+ const iv = randomBytes(12);
8
+ const cipher = createCipheriv('aes-256-gcm', key(secret), iv);
9
+ const enc = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
10
+ return Buffer.concat([iv, cipher.getAuthTag(), enc]).toString('base64url');
11
+ }
12
+
13
+ export function decrypt(payload: string, secret: string): string {
14
+ const buf = Buffer.from(payload, 'base64url');
15
+ const decipher = createDecipheriv('aes-256-gcm', key(secret), buf.subarray(0, 12));
16
+ decipher.setAuthTag(buf.subarray(12, 28));
17
+ return Buffer.concat([decipher.update(buf.subarray(28)), decipher.final()]).toString('utf8');
18
+ }
19
+
20
+ export function parseCookies(header: string | undefined | null): Record<string, string> {
21
+ const out: Record<string, string> = {};
22
+ if (!header) return out;
23
+ for (const part of header.split(';')) {
24
+ const idx = part.indexOf('=');
25
+ if (idx === -1) continue;
26
+ const k = part.slice(0, idx).trim();
27
+ const v = part.slice(idx + 1).trim();
28
+ if (!k) continue;
29
+ try {
30
+ out[k] = decodeURIComponent(v);
31
+ } catch {
32
+
33
+ }
34
+ }
35
+ return out;
36
+ }
37
+
38
+ export function readTokenFromCookieHeader(
39
+ header: string | undefined | null,
40
+ cookieName: string,
41
+ secret: string,
42
+ ): string | null {
43
+ const raw = parseCookies(header)[cookieName];
44
+ if (!raw) return null;
45
+ try {
46
+ return decrypt(raw, secret);
47
+ } catch {
48
+ return null;
49
+ }
50
+ }
package/src/lib/env.ts ADDED
@@ -0,0 +1,3 @@
1
+
2
+ export const env = (key: string): string | undefined =>
3
+ process.env[key]?.trim() || undefined;
@@ -0,0 +1,55 @@
1
+ const GITHUB_API = 'https://api.github.com';
2
+
3
+ export interface GithubFetchOptions {
4
+ token?: string;
5
+
6
+ timeoutMs?: number;
7
+ method?: string;
8
+ body?: BodyInit | null;
9
+ headers?: Record<string, string>;
10
+ }
11
+
12
+ export function githubHeaders(token?: string): Record<string, string> {
13
+ return {
14
+ Accept: 'application/vnd.github+json',
15
+ 'X-GitHub-Api-Version': '2022-11-28',
16
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
17
+ };
18
+ }
19
+
20
+ export function githubFetch(
21
+ path: `/${string}`,
22
+ options: GithubFetchOptions = {},
23
+ ): Promise<Response> {
24
+ return fetch(`${GITHUB_API}${path}`, {
25
+ method: options.method,
26
+ body: options.body,
27
+ redirect: 'error',
28
+ headers: { ...githubHeaders(options.token), ...options.headers },
29
+ signal: options.timeoutMs ? AbortSignal.timeout(options.timeoutMs) : undefined,
30
+ });
31
+ }
32
+
33
+ export async function githubGraphql<T>(
34
+ query: string,
35
+ variables: Record<string, unknown>,
36
+ options: Pick<GithubFetchOptions, 'token'> & { userAgent?: string },
37
+ ): Promise<T> {
38
+ const res = await fetch(`${GITHUB_API}/graphql`, {
39
+ method: 'POST',
40
+ headers: {
41
+ Authorization: `Bearer ${options.token}`,
42
+ 'Content-Type': 'application/json',
43
+ 'User-Agent': options.userAgent ?? '@terrafirmagreg-team/data',
44
+ },
45
+ body: JSON.stringify({ query, variables }),
46
+ cache: 'no-store',
47
+ });
48
+ if (!res.ok) throw new Error(`GitHub GraphQL ${res.status}: ${await res.text()}`);
49
+ const json = (await res.json()) as { data?: T; errors?: { message: string }[] };
50
+ if (json.errors?.length) {
51
+ throw new Error(`GitHub GraphQL: ${json.errors.map((e) => e.message).join('; ')}`);
52
+ }
53
+ if (!json.data) throw new Error('GitHub GraphQL: empty response');
54
+ return json.data;
55
+ }
@@ -0,0 +1,15 @@
1
+ export { env } from './env';
2
+ export { kvBinding, type KvBinding } from './kv';
3
+ export { loadJsonFile, saveJsonFile, jsonFileStore } from './json-file';
4
+ export {
5
+ githubFetch,
6
+ githubGraphql,
7
+ githubHeaders,
8
+ type GithubFetchOptions,
9
+ } from './github';
10
+ export {
11
+ encrypt,
12
+ decrypt,
13
+ parseCookies,
14
+ readTokenFromCookieHeader,
15
+ } from './cookie';
@@ -0,0 +1,31 @@
1
+ import 'server-only';
2
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
+ import { dirname } from 'node:path';
4
+
5
+ export function loadJsonFile<T>(file: string, fallback: () => T): T {
6
+ try {
7
+ return JSON.parse(
8
+ readFileSync( file, 'utf8'),
9
+ ) as T;
10
+ } catch {
11
+ return fallback();
12
+ }
13
+ }
14
+
15
+ export function saveJsonFile(file: string, data: unknown, space?: number): void {
16
+ mkdirSync(dirname(file), { recursive: true });
17
+ writeFileSync(file, JSON.stringify(data, null, space));
18
+ }
19
+
20
+ export function jsonFileStore<T>(file: string, fallback: () => T, space?: number) {
21
+ let cached: T | undefined;
22
+ const store = {
23
+ get data(): T {
24
+ return (cached ??= loadJsonFile(file, fallback));
25
+ },
26
+ save(): void {
27
+ saveJsonFile(file, store.data, space);
28
+ },
29
+ };
30
+ return store;
31
+ }
package/src/lib/kv.ts ADDED
@@ -0,0 +1,15 @@
1
+ export type KvBinding = {
2
+ get(key: string): Promise<string | null>;
3
+ put(
4
+ key: string,
5
+ value: string,
6
+ opts?: { expirationTtl?: number },
7
+ ): Promise<void>;
8
+ };
9
+
10
+ export function kvBinding(name: string): KvBinding | undefined {
11
+ const kv = (process.env as Record<string, unknown>)[name];
12
+ return kv && typeof (kv as KvBinding).get === 'function'
13
+ ? (kv as KvBinding)
14
+ : undefined;
15
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.