Template
1
0

feat: add payment module

This commit is contained in:
2025-12-05 01:56:42 +01:00
parent a818f3135a
commit be9b8e9e55
160 changed files with 8615 additions and 1158 deletions

View File

@@ -1,6 +1,8 @@
import { join } from "node:path";
import { load } from "@std/dotenv";
const env = await load();
const env = await load({ envPath: getDotEnvPath(), export: true });
/**
* TODO ...
@@ -8,3 +10,11 @@ const env = await load();
export function getDotEnvVariable(key: string): string {
return env[key] ?? Deno.env.get(key);
}
function getDotEnvPath(): string {
const dirname = import.meta.dirname;
if (dirname === undefined) {
throw new Error("Unable to determine dirname in config");
}
return join(dirname, "..", "..", ".env");
}

View File

@@ -1,11 +1,9 @@
import { load } from "@std/dotenv";
import type { ZodType, z } from "zod";
import { getDotEnvVariable } from "./dotenv.ts";
import { InvalidEnvironmentKeyError } from "./errors.ts";
import { getServiceEnvironment, type ServiceEnvironment } from "./service.ts";
const env = await load();
/**
* TODO ...
*/
@@ -21,7 +19,7 @@ export function getEnvironmentVariable<TType extends ZodType>({
fallback?: string;
}): z.infer<TType> {
const serviceEnv = getServiceEnvironment();
const providedValue = env[key] ?? Deno.env.get(key);
const providedValue = getDotEnvVariable(key);
const fallbackValue = typeof envFallback === "object" ? (envFallback[serviceEnv] ?? fallback) : fallback;
const toBeUsed = providedValue ?? fallbackValue;
try {

View File

@@ -9,6 +9,6 @@
},
"dependencies": {
"@std/dotenv": "npm:@jsr/std__dotenv@0.225.5",
"zod": "4.1.12"
"zod": "4.1.13"
}
}

View File

@@ -1,12 +1,16 @@
import { AsyncLocalStorage } from "node:async_hooks";
import { serialize } from "node:v8";
import { takeAll, takeOne } from "@platform/parse";
import postgres, { type Options, type Sql, type TransactionSql } from "postgres";
import transit from "transit-js";
import type { ZodType } from "zod";
import { takeAll, takeOne } from "./parser.ts";
const storage = new AsyncLocalStorage<TransactionSql>();
const transitReader = transit.reader("json");
const transitWriter = transit.writer("json");
/*
|--------------------------------------------------------------------------------
| Database
@@ -24,7 +28,16 @@ export class Client {
*
* @param db - Dependency container token to retrieve.
*/
constructor(readonly config: Options<{}>) {}
constructor(
readonly config: Options<{
transit: {
to: 16384;
from: [16384];
serialize: (value: unknown) => string;
parse: (value: unknown) => any;
};
}>,
) {}
/**
* SQL instance to perform queries against.
@@ -51,7 +64,34 @@ export class Client {
*/
#getResolvedInstance(): Sql {
if (this.#db === undefined) {
this.#db = postgres(this.config);
this.#db = postgres({
...this.config,
connection: {
fallback_output_format: "transit",
},
types: {
transit: {
to: 16384,
from: [16384],
serialize: (value: unknown) => {
return transitWriter.write(value);
},
parse: (value: string) => {
return transitReader.read(value);
},
},
int64: {
from: [20],
parse: (value: string) => {
const res = parseInt(value, 10);
if (Number.isSafeInteger(res) === false) {
throw Error(`Could not convert to integer reliably: ${value}`);
}
return res;
},
} as unknown as postgres.PostgresType,
},
});
}
return this.#db;
}
@@ -106,6 +146,30 @@ export class Client {
many: (strings: TemplateStringsArray, ...values: any[]) => this.sql(strings, ...values).then(takeAll(schema)),
};
}
boolean(value: boolean) {
return this.sql.typed(value, 16);
}
int64(value: number) {
return this.sql.typed(value, 20);
}
int32(value: number) {
return this.sql.typed(value, 23);
}
text(value: string) {
return this.sql.typed(value, 25);
}
float64(value: number) {
return this.sql.typed(value, 701);
}
transit(value: object) {
return this.sql.typed(value, 16384);
}
}
/*

View File

@@ -1,27 +1,25 @@
import { getEnvironmentVariable } from "@platform/config/environment.ts";
import { getEnvironmentVariable } from "@platform/config";
import z from "zod";
export const config = {
xtdb: {
host: getEnvironmentVariable({
key: "DB_XTDB_HOST",
type: z.string(),
fallback: "localhost",
}),
port: getEnvironmentVariable({
key: "DB_XTDB_PORT",
type: z.coerce.number(),
fallback: "5432",
}),
user: getEnvironmentVariable({
key: "DB_XTDB_USER",
type: z.string(),
fallback: "xtdb",
}),
pass: getEnvironmentVariable({
key: "DB_XTDB_PASSWORD",
type: z.string(),
fallback: "xtdb",
}),
},
host: getEnvironmentVariable({
key: "DB_XTDB_HOST",
type: z.string(),
fallback: "localhost",
}),
port: getEnvironmentVariable({
key: "DB_XTDB_PORT",
type: z.coerce.number(),
fallback: "5432",
}),
user: getEnvironmentVariable({
key: "DB_XTDB_USER",
type: z.string(),
fallback: "xtdb",
}),
pass: getEnvironmentVariable({
key: "DB_XTDB_PASSWORD",
type: z.string(),
fallback: "xtdb",
}),
};

6
platform/database/mod.ts Normal file
View File

@@ -0,0 +1,6 @@
import { Client } from "./client.ts";
import { config } from "./config.ts";
export * from "./client.ts";
export const db = new Client(config);

View File

@@ -3,9 +3,16 @@
"version": "0.0.0",
"private": true,
"type": "module",
"main": "./mod.ts",
"exports": {
".": "./mod.ts"
},
"dependencies": {
"@platform/config": "workspace:*",
"@platform/parse": "workspace:*",
"@types/transit-js": "0.8.3",
"postgres": "3.4.7",
"zod": "4.1.12"
"transit-js": "0.8.874",
"zod": "4.1.13"
}
}

View File

@@ -1,29 +0,0 @@
import type z from "zod";
import type { ZodType } from "zod";
/**
* Takes a single record from a list of database rows.
*
* @param rows - List of rows to retrieve record from.
*/
export function takeOne<TSchema extends ZodType>(
schema: TSchema,
): (records: unknown[]) => z.output<TSchema> | undefined {
return (records: unknown[]) => {
if (records[0] === undefined) {
return undefined;
}
return schema.parse(records[0]);
};
}
/**
* Takes all records from a list of database rows and validates each one.
*
* @param schema - Zod schema to validate each record against.
*/
export function takeAll<TSchema extends ZodType>(schema: TSchema): (records: unknown[]) => z.output<TSchema>[] {
return (records: unknown[]) => {
return records.map((record) => schema.parse(record));
};
}

View File

@@ -10,6 +10,6 @@
"dependencies": {
"@platform/config": "workspace:*",
"@valkyr/event-store": "npm:@jsr/valkyr__event-store@2.0.1",
"zod": "4.1.12"
"zod": "4.1.13"
}
}

1
platform/parse/mod.ts Normal file
View File

@@ -0,0 +1 @@
export * from "./zod.ts";

View File

@@ -0,0 +1,13 @@
{
"name": "@platform/parse",
"version": "0.0.0",
"private": true,
"type": "module",
"main": "./mod.ts",
"exports": {
".": "./mod.ts"
},
"dependencies": {
"zod": "4.1.13"
}
}

13
platform/parse/time.ts Normal file
View File

@@ -0,0 +1,13 @@
import z from "zod";
export const ZonedDateTimeSchema = z.object({
tag: z.literal("time/zoned-date-time"),
rep: z.string(),
hashCode: z.number(),
});
export function assertZonedDateTime(value: object | null): value is ZonedDateTime {
return ZonedDateTimeSchema.parse(value) !== undefined;
}
export type ZonedDateTime = z.output<typeof ZonedDateTimeSchema>;

66
platform/parse/zod.ts Normal file
View File

@@ -0,0 +1,66 @@
import z, { type ZodType } from "zod";
import { assertZonedDateTime } from "./time.ts";
/**
* Takes a single record from a list of database rows.
*
* @param rows - List of rows to retrieve record from.
*/
export function takeOne<TSchema extends ZodType>(
schema: TSchema,
): (records: unknown[]) => z.output<TSchema> | undefined {
return (records: unknown[]) => {
if (records[0] === undefined) {
return undefined;
}
return schema.parse(records[0]);
};
}
/**
* Takes all records from a list of database rows and validates each one.
*
* @param schema - Zod schema to validate each record against.
*/
export function takeAll<TSchema extends ZodType>(schema: TSchema): (records: unknown[]) => z.output<TSchema>[] {
return (records: unknown[]) => {
return records.map((record) => schema.parse(record));
};
}
/**
* Maps a XTDB NEST_MANY sub query to the provided schema.
*
* @param schema - Schema to parse the NEST_MANY result against.
*/
export function nestMany<TSchema extends ZodType>(schema: TSchema) {
return z.array(z.preprocess(xtdbEntriesToObject, schema)).default([]);
}
/**
* Check for a _entries key on the provided record and re-map them to
* a new object if _entries exists.
*
* @param record - Record to re-map.
*/
function xtdbEntriesToObject(record: any) {
if ("_entries" in record) {
const obj: any = {};
for (let i = 0; i < record._entries.length; i += 2) {
obj[record._entries[i]] = xtdbEntryValue(record._entries[i + 1]);
}
return obj;
}
return record;
}
function xtdbEntryValue(value: unknown) {
if (Array.isArray(value) === true) {
return value;
}
if (typeof value === "object" && assertZonedDateTime(value) === true) {
return value.rep.replace("[UTC]", "");
}
return value;
}

View File

@@ -12,6 +12,6 @@
"@platform/socket": "workspace:*",
"@platform/supertokens": "workspace:*",
"path-to-regexp": "8",
"zod": "4.1.12"
"zod": "4.1.13"
}
}

View File

@@ -5,6 +5,6 @@
"type": "module",
"dependencies": {
"@platform/relay": "workspace:*",
"zod": "4.1.12"
"zod": "4.1.13"
}
}

View File

@@ -11,6 +11,6 @@
"@platform/socket": "workspace:*",
"@platform/storage": "workspace:*",
"@valkyr/json-rpc": "npm:@jsr/valkyr__json-rpc@1.1.0",
"zod": "4.1.12"
"zod": "4.1.13"
}
}

View File

@@ -6,6 +6,6 @@
"dependencies": {
"@platform/models": "workspace:*",
"@platform/relay": "workspace:*",
"zod": "4.1.12"
"zod": "4.1.13"
}
}