pecunia-root 0.0.3
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/dist/adapters/drizzle/index.d.mts +47 -0
- package/dist/adapters/drizzle/index.mjs +218 -0
- package/dist/adapters/drizzle/index.mjs.map +1 -0
- package/dist/adapters/get-adapter.d.mts +7 -0
- package/dist/adapters/get-adapter.mjs +31 -0
- package/dist/adapters/get-adapter.mjs.map +1 -0
- package/dist/adapters/internal/index.mjs +11 -0
- package/dist/adapters/internal/index.mjs.map +1 -0
- package/dist/adapters/kysely/bun-sqlite-dialect.mjs +156 -0
- package/dist/adapters/kysely/bun-sqlite-dialect.mjs.map +1 -0
- package/dist/adapters/kysely/dialect.mjs +83 -0
- package/dist/adapters/kysely/dialect.mjs.map +1 -0
- package/dist/adapters/kysely/index.d.mts +34 -0
- package/dist/adapters/kysely/index.mjs +183 -0
- package/dist/adapters/kysely/index.mjs.map +1 -0
- package/dist/adapters/kysely/node-sqlite-dialect.mjs +156 -0
- package/dist/adapters/kysely/node-sqlite-dialect.mjs.map +1 -0
- package/dist/adapters/mongodb/index.d.mts +35 -0
- package/dist/adapters/mongodb/index.mjs +313 -0
- package/dist/adapters/mongodb/index.mjs.map +1 -0
- package/dist/adapters/prisma/index.d.mts +34 -0
- package/dist/adapters/prisma/index.mjs +213 -0
- package/dist/adapters/prisma/index.mjs.map +1 -0
- package/dist/api/index.d.mts +23 -0
- package/dist/api/index.mjs +126 -0
- package/dist/api/index.mjs.map +1 -0
- package/dist/context/index.mjs +77 -0
- package/dist/context/index.mjs.map +1 -0
- package/dist/db/index.d.mts +3 -0
- package/dist/db/index.mjs +4 -0
- package/dist/db/migrations/index.d.mts +21 -0
- package/dist/db/migrations/index.mjs +327 -0
- package/dist/db/migrations/index.mjs.map +1 -0
- package/dist/db/schema/get-schema.d.mts +10 -0
- package/dist/db/schema/get-schema.mjs +39 -0
- package/dist/db/schema/get-schema.mjs.map +1 -0
- package/dist/index.d.mts +9 -0
- package/dist/index.mjs +7 -0
- package/dist/payment/base.mjs +38 -0
- package/dist/payment/base.mjs.map +1 -0
- package/dist/payment/index.d.mts +21 -0
- package/dist/payment/index.mjs +23 -0
- package/dist/payment/index.mjs.map +1 -0
- package/dist/types/payment.d.mts +11 -0
- package/dist/types/payment.mjs +1 -0
- package/dist/utils/is-promise.mjs +8 -0
- package/dist/utils/is-promise.mjs.map +1 -0
- package/dist/utils/url.mjs +77 -0
- package/dist/utils/url.mjs.map +1 -0
- package/package.json +183 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { DBFieldAttribute, PecuniaOptions } from "pecunia-core";
|
|
2
|
+
|
|
3
|
+
//#region src/db/schema/get-schema.d.ts
|
|
4
|
+
declare function getSchema(config: PecuniaOptions): Record<string, {
|
|
5
|
+
fields: Record<string, DBFieldAttribute>;
|
|
6
|
+
order: number;
|
|
7
|
+
}>;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { getSchema };
|
|
10
|
+
//# sourceMappingURL=get-schema.d.mts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { getPaymentTables } from "pecunia-core";
|
|
2
|
+
|
|
3
|
+
//#region src/db/schema/get-schema.ts
|
|
4
|
+
function getSchema(config) {
|
|
5
|
+
const tables = getPaymentTables(config);
|
|
6
|
+
let schema = {};
|
|
7
|
+
for (const key in tables) {
|
|
8
|
+
const table = tables[key];
|
|
9
|
+
const fields = table.fields;
|
|
10
|
+
let actualFields = {};
|
|
11
|
+
Object.entries(fields).forEach(([key$1, field]) => {
|
|
12
|
+
actualFields[field.fieldName || key$1] = field;
|
|
13
|
+
if (field.references) {
|
|
14
|
+
const refTable = tables[field.references.model];
|
|
15
|
+
if (refTable) actualFields[field.fieldName || key$1].references = {
|
|
16
|
+
...field.references,
|
|
17
|
+
model: refTable.modelName,
|
|
18
|
+
field: field.references.field
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
if (schema[table.modelName]) {
|
|
23
|
+
schema[table.modelName].fields = {
|
|
24
|
+
...schema[table.modelName].fields,
|
|
25
|
+
...actualFields
|
|
26
|
+
};
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
schema[table.modelName] = {
|
|
30
|
+
fields: actualFields,
|
|
31
|
+
order: table.order || Infinity
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return schema;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
export { getSchema };
|
|
39
|
+
//# sourceMappingURL=get-schema.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-schema.mjs","names":["schema: Record<\n string,\n {\n fields: Record<string, DBFieldAttribute>;\n order: number;\n }\n >","actualFields: Record<string, DBFieldAttribute>","key"],"sources":["../../../src/db/schema/get-schema.ts"],"sourcesContent":["import { getPaymentTables } from \"pecunia-core\";\nimport type { PecuniaOptions } from \"pecunia-core\";\nimport type { DBFieldAttribute } from \"pecunia-core\";\n\nexport function getSchema(config: PecuniaOptions) {\n const tables = getPaymentTables(config);\n let schema: Record<\n string,\n {\n fields: Record<string, DBFieldAttribute>;\n order: number;\n }\n > = {};\n for (const key in tables) {\n const table = tables[key]!;\n const fields = table.fields;\n let actualFields: Record<string, DBFieldAttribute> = {};\n Object.entries(fields).forEach(([key, field]) => {\n actualFields[field.fieldName || key] = field;\n if (field.references) {\n const refTable = tables[field.references.model];\n if (refTable) {\n actualFields[field.fieldName || key]!.references = {\n ...field.references,\n model: refTable.modelName,\n field: field.references.field,\n };\n }\n }\n });\n if (schema[table.modelName]) {\n schema[table.modelName]!.fields = {\n ...schema[table.modelName]!.fields,\n ...actualFields,\n };\n continue;\n }\n schema[table.modelName] = {\n fields: actualFields,\n order: table.order || Infinity,\n };\n }\n return schema;\n}\n"],"mappings":";;;AAIA,SAAgB,UAAU,QAAwB;CAChD,MAAM,SAAS,iBAAiB,OAAO;CACvC,IAAIA,SAMA,EAAE;AACN,MAAK,MAAM,OAAO,QAAQ;EACxB,MAAM,QAAQ,OAAO;EACrB,MAAM,SAAS,MAAM;EACrB,IAAIC,eAAiD,EAAE;AACvD,SAAO,QAAQ,OAAO,CAAC,SAAS,CAACC,OAAK,WAAW;AAC/C,gBAAa,MAAM,aAAaA,SAAO;AACvC,OAAI,MAAM,YAAY;IACpB,MAAM,WAAW,OAAO,MAAM,WAAW;AACzC,QAAI,SACF,cAAa,MAAM,aAAaA,OAAM,aAAa;KACjD,GAAG,MAAM;KACT,OAAO,SAAS;KAChB,OAAO,MAAM,WAAW;KACzB;;IAGL;AACF,MAAI,OAAO,MAAM,YAAY;AAC3B,UAAO,MAAM,WAAY,SAAS;IAChC,GAAG,OAAO,MAAM,WAAY;IAC5B,GAAG;IACJ;AACD;;AAEF,SAAO,MAAM,aAAa;GACxB,QAAQ;GACR,OAAO,MAAM,SAAS;GACvB;;AAEH,QAAO"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { getMigrations, matchType } from "./db/migrations/index.mjs";
|
|
2
|
+
import { getSchema } from "./db/schema/get-schema.mjs";
|
|
3
|
+
import "./db/index.mjs";
|
|
4
|
+
import { pecunia } from "./payment/index.mjs";
|
|
5
|
+
import { getAdapter } from "./adapters/get-adapter.mjs";
|
|
6
|
+
export * from "zod";
|
|
7
|
+
export * from "zod/v4";
|
|
8
|
+
export * from "zod/v4/core";
|
|
9
|
+
export { getAdapter, getMigrations, getSchema, matchType, pecunia };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { getSchema } from "./db/schema/get-schema.mjs";
|
|
2
|
+
import { getMigrations, matchType } from "./db/migrations/index.mjs";
|
|
3
|
+
import "./db/index.mjs";
|
|
4
|
+
import { getAdapter } from "./adapters/get-adapter.mjs";
|
|
5
|
+
import { pecunia } from "./payment/index.mjs";
|
|
6
|
+
|
|
7
|
+
export { getAdapter, getMigrations, getSchema, matchType, pecunia };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { getBaseURL, getOrigin } from "../utils/url.mjs";
|
|
2
|
+
import { getEndpoints, router } from "../api/index.mjs";
|
|
3
|
+
import { PecuniaError, runWithAdapter } from "pecunia-core";
|
|
4
|
+
|
|
5
|
+
//#region src/payment/base.ts
|
|
6
|
+
const createPecunia = (options, initFn) => {
|
|
7
|
+
const paymentContext = initFn(options);
|
|
8
|
+
const { api } = getEndpoints(paymentContext, options);
|
|
9
|
+
return {
|
|
10
|
+
handler: async (request) => {
|
|
11
|
+
const ctx = await paymentContext;
|
|
12
|
+
const basePath = ctx.options.basePath || "/api/payment";
|
|
13
|
+
if (!ctx.options.baseURL) {
|
|
14
|
+
const baseURL = getBaseURL(void 0, basePath, request, void 0);
|
|
15
|
+
if (baseURL) {
|
|
16
|
+
ctx.baseURL = baseURL;
|
|
17
|
+
ctx.options.baseURL = getOrigin(ctx.baseURL) || void 0;
|
|
18
|
+
} else throw new PecuniaError("Could not get base URL from request. Please provide a valid base URL.");
|
|
19
|
+
}
|
|
20
|
+
const { handler } = router(ctx, options);
|
|
21
|
+
return runWithAdapter(ctx.adapter, () => handler(request));
|
|
22
|
+
},
|
|
23
|
+
api,
|
|
24
|
+
options,
|
|
25
|
+
$context: paymentContext,
|
|
26
|
+
$ERROR_CODES: { ...options.plugins?.reduce((acc, plugin) => {
|
|
27
|
+
if (plugin.$ERROR_CODES) return {
|
|
28
|
+
...acc,
|
|
29
|
+
...plugin.$ERROR_CODES
|
|
30
|
+
};
|
|
31
|
+
return acc;
|
|
32
|
+
}, {}) }
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
//#endregion
|
|
37
|
+
export { createPecunia };
|
|
38
|
+
//# sourceMappingURL=base.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.mjs","names":[],"sources":["../../src/payment/base.ts"],"sourcesContent":["import type { PecuniaContext, PecuniaOptions } from \"pecunia-core\";\nimport { runWithAdapter } from \"pecunia-core\";\nimport { PecuniaError } from \"pecunia-core\";\nimport { getEndpoints, router } from \"../api\";\n// import { getTrustedOrigins } from \"../context/helpers\";\n// import type { Auth } from \"../types\";\nimport { getBaseURL, getOrigin } from \"../utils/url\";\n\nexport const createPecunia = <Options extends PecuniaOptions>(\n\toptions: Options &\n\t\tRecord<never, never>,\n\tinitFn: (options: Options) => Promise<PecuniaContext>,\n) => {\n\tconst paymentContext = initFn(options);\n\tconst { api } = getEndpoints(paymentContext, options);\n\tconst errorCodes = options.plugins?.reduce((acc, plugin) => {\n\t\tif (plugin.$ERROR_CODES) {\n\t\t\treturn {\n\t\t\t\t...acc,\n\t\t\t\t...plugin.$ERROR_CODES,\n\t\t\t};\n\t\t}\n\t\treturn acc;\n\t}, {});\n\treturn {\n\t\thandler: async (request: Request) => {\n\t\t\tconst ctx = await paymentContext;\n\t\t\tconst basePath = ctx.options.basePath || \"/api/payment\";\n\t\t\tif (!ctx.options.baseURL) {\n\t\t\t\tconst baseURL = getBaseURL(\n\t\t\t\t\tundefined,\n\t\t\t\t\tbasePath,\n\t\t\t\t\trequest,\n\t\t\t\t\tundefined\n\t\t\t\t);\n\t\t\t\tif (baseURL) {\n\t\t\t\t\tctx.baseURL = baseURL;\n\t\t\t\t\tctx.options.baseURL = getOrigin(ctx.baseURL) || undefined;\n\t\t\t\t} else {\n\t\t\t\t\tthrow new PecuniaError(\n\t\t\t\t\t\t\"Could not get base URL from request. Please provide a valid base URL.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// ctx.trustedOrigins = await getTrustedOrigins(ctx.options, request);\n\t\t\tconst { handler } = router(ctx, options);\n\t\t\treturn runWithAdapter(ctx.adapter, () => handler(request));\n\t\t},\n\t\tapi,\n\t\toptions: options,\n\t\t$context: paymentContext,\n\t\t$ERROR_CODES: {\n\t\t\t...errorCodes\n\t\t},\n\t} as any;\n};\n"],"mappings":";;;;;AAQA,MAAa,iBACZ,SAEA,WACI;CACJ,MAAM,iBAAiB,OAAO,QAAQ;CACtC,MAAM,EAAE,QAAQ,aAAa,gBAAgB,QAAQ;AAUrD,QAAO;EACN,SAAS,OAAO,YAAqB;GACpC,MAAM,MAAM,MAAM;GAClB,MAAM,WAAW,IAAI,QAAQ,YAAY;AACzC,OAAI,CAAC,IAAI,QAAQ,SAAS;IACzB,MAAM,UAAU,WACf,QACA,UACA,SACA,OACA;AACD,QAAI,SAAS;AACZ,SAAI,UAAU;AACd,SAAI,QAAQ,UAAU,UAAU,IAAI,QAAQ,IAAI;UAEhD,OAAM,IAAI,aACT,wEACA;;GAIH,MAAM,EAAE,YAAY,OAAO,KAAK,QAAQ;AACxC,UAAO,eAAe,IAAI,eAAe,QAAQ,QAAQ,CAAC;;EAE3D;EACS;EACT,UAAU;EACV,cAAc,EACb,GArCiB,QAAQ,SAAS,QAAQ,KAAK,WAAW;AAC3D,OAAI,OAAO,aACV,QAAO;IACN,GAAG;IACH,GAAG,OAAO;IACV;AAEF,UAAO;KACL,EAAE,CAAC,EA8BJ;EACD"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Payment } from "../types/payment.mjs";
|
|
2
|
+
import { PecuniaOptions } from "pecunia-core";
|
|
3
|
+
|
|
4
|
+
//#region src/payment/index.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Pecunia initializer for full mode (with Kysely)
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { pecunia } from "@pecunia";
|
|
12
|
+
*
|
|
13
|
+
* const payment = pecunia({
|
|
14
|
+
* database: new PostgresDialect({ connection: process.env.DATABASE_URL }),
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
declare const pecunia: <Options extends PecuniaOptions>(options: Options & Record<never, never>) => Payment<Options>;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { pecunia };
|
|
21
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { init } from "../context/index.mjs";
|
|
2
|
+
import { createPecunia } from "./base.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/payment/index.ts
|
|
5
|
+
/**
|
|
6
|
+
* Pecunia initializer for full mode (with Kysely)
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { pecunia } from "@pecunia";
|
|
11
|
+
*
|
|
12
|
+
* const payment = pecunia({
|
|
13
|
+
* database: new PostgresDialect({ connection: process.env.DATABASE_URL }),
|
|
14
|
+
* });
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
const pecunia = (options) => {
|
|
18
|
+
return createPecunia(options, init);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
export { pecunia };
|
|
23
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/payment/index.ts"],"sourcesContent":["import type { PecuniaOptions } from \"pecunia-core\";\nimport { init } from \"../context\";\nimport type { Payment } from \"../types/payment\";\nimport { createPecunia } from \"./base\";\n\n/**\n * Pecunia initializer for full mode (with Kysely)\n *\n * @example\n * ```ts\n * import { pecunia } from \"@pecunia\";\n *\n * const payment = pecunia({\n * \tdatabase: new PostgresDialect({ connection: process.env.DATABASE_URL }),\n * });\n * ```\n */\nexport const pecunia = <Options extends PecuniaOptions>(\n\toptions: Options & Record<never, never>,\n): Payment<Options> => {\n\treturn createPecunia(options, init);\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAiBA,MAAa,WACZ,YACsB;AACtB,QAAO,cAAc,SAAS,KAAK"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PecuniaContext, PecuniaOptions } from "pecunia-core";
|
|
2
|
+
|
|
3
|
+
//#region src/types/payment.d.ts
|
|
4
|
+
type Payment<Options extends PecuniaOptions = PecuniaOptions> = {
|
|
5
|
+
handler: (request: Request) => Promise<Response>;
|
|
6
|
+
options: Options;
|
|
7
|
+
$context: Promise<PecuniaContext>;
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export { Payment };
|
|
11
|
+
//# sourceMappingURL=payment.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-promise.mjs","names":[],"sources":["../../src/utils/is-promise.ts"],"sourcesContent":["export function isPromise(obj?: unknown): obj is Promise<unknown> {\n\treturn (\n\t\t!!obj &&\n\t\t(typeof obj === \"object\" || typeof obj === \"function\") &&\n\t\ttypeof (obj as Promise<unknown>).then === \"function\"\n\t);\n}\n"],"mappings":";AAAA,SAAgB,UAAU,KAAwC;AACjE,QACC,CAAC,CAAC,QACD,OAAO,QAAQ,YAAY,OAAO,QAAQ,eAC3C,OAAQ,IAAyB,SAAS"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { PecuniaError, env } from "pecunia-core";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/url.ts
|
|
4
|
+
function checkHasPath(url) {
|
|
5
|
+
try {
|
|
6
|
+
return (new URL(url).pathname.replace(/\/+$/, "") || "/") !== "/";
|
|
7
|
+
} catch {
|
|
8
|
+
throw new PecuniaError(`Invalid base URL: ${url}. Please provide a valid base URL.`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function assertHasProtocol(url) {
|
|
12
|
+
try {
|
|
13
|
+
const parsedUrl = new URL(url);
|
|
14
|
+
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new PecuniaError(`Invalid base URL: ${url}. URL must include 'http://' or 'https://'`);
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (error instanceof PecuniaError) throw error;
|
|
17
|
+
throw new PecuniaError(`Invalid base URL: ${url}. Please provide a valid base URL.`, String(error));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function withPath(url, path = "/api/auth") {
|
|
21
|
+
assertHasProtocol(url);
|
|
22
|
+
if (checkHasPath(url)) return url;
|
|
23
|
+
const trimmedUrl = url.replace(/\/+$/, "");
|
|
24
|
+
if (!path || path === "/") return trimmedUrl;
|
|
25
|
+
path = path.startsWith("/") ? path : `/${path}`;
|
|
26
|
+
return `${trimmedUrl}${path}`;
|
|
27
|
+
}
|
|
28
|
+
function validateProxyHeader(header, type) {
|
|
29
|
+
if (!header || header.trim() === "") return false;
|
|
30
|
+
if (type === "proto") return header === "http" || header === "https";
|
|
31
|
+
if (type === "host") {
|
|
32
|
+
if ([
|
|
33
|
+
/\.\./,
|
|
34
|
+
/\0/,
|
|
35
|
+
/[\s]/,
|
|
36
|
+
/^[.]/,
|
|
37
|
+
/[<>'"]/,
|
|
38
|
+
/javascript:/i,
|
|
39
|
+
/file:/i,
|
|
40
|
+
/data:/i
|
|
41
|
+
].some((pattern) => pattern.test(header))) return false;
|
|
42
|
+
return /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*(:[0-9]{1,5})?$/.test(header) || /^(\d{1,3}\.){3}\d{1,3}(:[0-9]{1,5})?$/.test(header) || /^\[[0-9a-fA-F:]+\](:[0-9]{1,5})?$/.test(header) || /^localhost(:[0-9]{1,5})?$/i.test(header);
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
function getBaseURL(url, path, request, loadEnv, trustedProxyHeaders) {
|
|
47
|
+
if (url) return withPath(url, path);
|
|
48
|
+
if (loadEnv !== false) {
|
|
49
|
+
const fromEnv = env.PECUNIA_URL || env.NEXT_PUBLIC_PECUNIA_URL || env.PUBLIC_PECUNIA_URL || env.NUXT_PUBLIC_PECUNIA_URL || env.NUXT_PUBLIC_AUTH_URL || (env.BASE_URL !== "/" ? env.BASE_URL : void 0);
|
|
50
|
+
if (fromEnv) return withPath(fromEnv, path);
|
|
51
|
+
}
|
|
52
|
+
const fromRequest = request?.headers.get("x-forwarded-host");
|
|
53
|
+
const fromRequestProto = request?.headers.get("x-forwarded-proto");
|
|
54
|
+
if (fromRequest && fromRequestProto && trustedProxyHeaders) {
|
|
55
|
+
if (validateProxyHeader(fromRequestProto, "proto") && validateProxyHeader(fromRequest, "host")) try {
|
|
56
|
+
return withPath(`${fromRequestProto}://${fromRequest}`, path);
|
|
57
|
+
} catch (_error) {}
|
|
58
|
+
}
|
|
59
|
+
if (request) {
|
|
60
|
+
const url$1 = getOrigin(request.url);
|
|
61
|
+
if (!url$1) throw new PecuniaError("Could not get origin from request. Please provide a valid base URL.");
|
|
62
|
+
return withPath(url$1, path);
|
|
63
|
+
}
|
|
64
|
+
if (typeof window !== "undefined" && window.location) return withPath(window.location.origin, path);
|
|
65
|
+
}
|
|
66
|
+
function getOrigin(url) {
|
|
67
|
+
try {
|
|
68
|
+
const parsedUrl = new URL(url);
|
|
69
|
+
return parsedUrl.origin === "null" ? null : parsedUrl.origin;
|
|
70
|
+
} catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
//#endregion
|
|
76
|
+
export { getBaseURL, getOrigin };
|
|
77
|
+
//# sourceMappingURL=url.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.mjs","names":["url"],"sources":["../../src/utils/url.ts"],"sourcesContent":["import { env } from \"pecunia-core\";\nimport { PecuniaError } from \"pecunia-core\";\n\nfunction checkHasPath(url: string): boolean {\n try {\n const parsedUrl = new URL(url);\n const pathname = parsedUrl.pathname.replace(/\\/+$/, \"\") || \"/\";\n return pathname !== \"/\";\n } catch {\n throw new PecuniaError(\n `Invalid base URL: ${url}. Please provide a valid base URL.`,\n );\n }\n}\n\nfunction assertHasProtocol(url: string): void {\n try {\n const parsedUrl = new URL(url);\n if (parsedUrl.protocol !== \"http:\" && parsedUrl.protocol !== \"https:\") {\n throw new PecuniaError(\n `Invalid base URL: ${url}. URL must include 'http://' or 'https://'`,\n );\n }\n } catch (error) {\n if (error instanceof PecuniaError) {\n throw error;\n }\n throw new PecuniaError(\n `Invalid base URL: ${url}. Please provide a valid base URL.`,\n String(error),\n );\n }\n}\n\nfunction withPath(url: string, path = \"/api/auth\") {\n assertHasProtocol(url);\n\n const hasPath = checkHasPath(url);\n if (hasPath) {\n return url;\n }\n\n const trimmedUrl = url.replace(/\\/+$/, \"\");\n\n if (!path || path === \"/\") {\n return trimmedUrl;\n }\n\n path = path.startsWith(\"/\") ? path : `/${path}`;\n return `${trimmedUrl}${path}`;\n}\n\nfunction validateProxyHeader(header: string, type: \"host\" | \"proto\"): boolean {\n if (!header || header.trim() === \"\") {\n return false;\n }\n\n if (type === \"proto\") {\n // Only allow http and https protocols\n return header === \"http\" || header === \"https\";\n }\n\n if (type === \"host\") {\n const suspiciousPatterns = [\n /\\.\\./, // Path traversal\n /\\0/, // Null bytes\n /[\\s]/, // Whitespace (except legitimate spaces that should be trimmed)\n /^[.]/, // Starting with dot\n /[<>'\"]/, // HTML/script injection characters\n /javascript:/i, // Protocol injection\n /file:/i, // File protocol\n /data:/i, // Data protocol\n ];\n\n if (suspiciousPatterns.some((pattern) => pattern.test(header))) {\n return false;\n }\n\n // Basic hostname validation (allows localhost, IPs, and domains with ports)\n // This is a simple check, not exhaustive RFC validation\n const hostnameRegex =\n /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*(:[0-9]{1,5})?$/;\n\n // Also allow IPv4 addresses\n const ipv4Regex = /^(\\d{1,3}\\.){3}\\d{1,3}(:[0-9]{1,5})?$/;\n\n // Also allow IPv6 addresses in brackets\n const ipv6Regex = /^\\[[0-9a-fA-F:]+\\](:[0-9]{1,5})?$/;\n\n // Allow localhost variations\n const localhostRegex = /^localhost(:[0-9]{1,5})?$/i;\n\n return (\n hostnameRegex.test(header) ||\n ipv4Regex.test(header) ||\n ipv6Regex.test(header) ||\n localhostRegex.test(header)\n );\n }\n\n return false;\n}\n\nexport function getBaseURL(\n url?: string,\n path?: string,\n request?: Request,\n loadEnv?: boolean,\n trustedProxyHeaders?: boolean | undefined,\n) {\n if (url) {\n return withPath(url, path);\n }\n\n if (loadEnv !== false) {\n const fromEnv =\n env.PECUNIA_URL ||\n env.NEXT_PUBLIC_PECUNIA_URL ||\n env.PUBLIC_PECUNIA_URL ||\n env.NUXT_PUBLIC_PECUNIA_URL ||\n env.NUXT_PUBLIC_AUTH_URL ||\n (env.BASE_URL !== \"/\" ? env.BASE_URL : undefined);\n\n if (fromEnv) {\n return withPath(fromEnv, path);\n }\n }\n\n const fromRequest = request?.headers.get(\"x-forwarded-host\");\n const fromRequestProto = request?.headers.get(\"x-forwarded-proto\");\n if (fromRequest && fromRequestProto && trustedProxyHeaders) {\n if (\n validateProxyHeader(fromRequestProto, \"proto\") &&\n validateProxyHeader(fromRequest, \"host\")\n ) {\n try {\n return withPath(`${fromRequestProto}://${fromRequest}`, path);\n } catch (_error) {}\n }\n }\n\n if (request) {\n const url = getOrigin(request.url);\n if (!url) {\n throw new PecuniaError(\n \"Could not get origin from request. Please provide a valid base URL.\",\n );\n }\n return withPath(url, path);\n }\n\n if (typeof window !== \"undefined\" && window.location) {\n return withPath(window.location.origin, path);\n }\n return undefined;\n}\n\nexport function getOrigin(url: string) {\n try {\n const parsedUrl = new URL(url);\n // For custom URL schemes (like exp://), the origin property returns the string \"null\"\n // instead of null. We need to handle this case and return null so the fallback logic works.\n return parsedUrl.origin === \"null\" ? null : parsedUrl.origin;\n } catch {\n return null;\n }\n}\n\nexport function getProtocol(url: string) {\n try {\n const parsedUrl = new URL(url);\n return parsedUrl.protocol;\n } catch {\n return null;\n }\n}\n\nexport function getHost(url: string) {\n try {\n const parsedUrl = new URL(url);\n return parsedUrl.host;\n } catch {\n return null;\n }\n}\n"],"mappings":";;;AAGA,SAAS,aAAa,KAAsB;AAC1C,KAAI;AAGF,UAFkB,IAAI,IAAI,IAAI,CACH,SAAS,QAAQ,QAAQ,GAAG,IAAI,SACvC;SACd;AACN,QAAM,IAAI,aACR,qBAAqB,IAAI,oCAC1B;;;AAIL,SAAS,kBAAkB,KAAmB;AAC5C,KAAI;EACF,MAAM,YAAY,IAAI,IAAI,IAAI;AAC9B,MAAI,UAAU,aAAa,WAAW,UAAU,aAAa,SAC3D,OAAM,IAAI,aACR,qBAAqB,IAAI,4CAC1B;UAEI,OAAO;AACd,MAAI,iBAAiB,aACnB,OAAM;AAER,QAAM,IAAI,aACR,qBAAqB,IAAI,qCACzB,OAAO,MAAM,CACd;;;AAIL,SAAS,SAAS,KAAa,OAAO,aAAa;AACjD,mBAAkB,IAAI;AAGtB,KADgB,aAAa,IAAI,CAE/B,QAAO;CAGT,MAAM,aAAa,IAAI,QAAQ,QAAQ,GAAG;AAE1C,KAAI,CAAC,QAAQ,SAAS,IACpB,QAAO;AAGT,QAAO,KAAK,WAAW,IAAI,GAAG,OAAO,IAAI;AACzC,QAAO,GAAG,aAAa;;AAGzB,SAAS,oBAAoB,QAAgB,MAAiC;AAC5E,KAAI,CAAC,UAAU,OAAO,MAAM,KAAK,GAC/B,QAAO;AAGT,KAAI,SAAS,QAEX,QAAO,WAAW,UAAU,WAAW;AAGzC,KAAI,SAAS,QAAQ;AAYnB,MAX2B;GACzB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAEsB,MAAM,YAAY,QAAQ,KAAK,OAAO,CAAC,CAC5D,QAAO;AAiBT,SAXE,8GAYc,KAAK,OAAO,IATV,wCAUN,KAAK,OAAO,IAPN,oCAQN,KAAK,OAAO,IALD,6BAMN,KAAK,OAAO;;AAI/B,QAAO;;AAGT,SAAgB,WACd,KACA,MACA,SACA,SACA,qBACA;AACA,KAAI,IACF,QAAO,SAAS,KAAK,KAAK;AAG5B,KAAI,YAAY,OAAO;EACrB,MAAM,UACJ,IAAI,eACJ,IAAI,2BACJ,IAAI,sBACJ,IAAI,2BACJ,IAAI,yBACH,IAAI,aAAa,MAAM,IAAI,WAAW;AAEzC,MAAI,QACF,QAAO,SAAS,SAAS,KAAK;;CAIlC,MAAM,cAAc,SAAS,QAAQ,IAAI,mBAAmB;CAC5D,MAAM,mBAAmB,SAAS,QAAQ,IAAI,oBAAoB;AAClE,KAAI,eAAe,oBAAoB,qBACrC;MACE,oBAAoB,kBAAkB,QAAQ,IAC9C,oBAAoB,aAAa,OAAO,CAExC,KAAI;AACF,UAAO,SAAS,GAAG,iBAAiB,KAAK,eAAe,KAAK;WACtD,QAAQ;;AAIrB,KAAI,SAAS;EACX,MAAMA,QAAM,UAAU,QAAQ,IAAI;AAClC,MAAI,CAACA,MACH,OAAM,IAAI,aACR,sEACD;AAEH,SAAO,SAASA,OAAK,KAAK;;AAG5B,KAAI,OAAO,WAAW,eAAe,OAAO,SAC1C,QAAO,SAAS,OAAO,SAAS,QAAQ,KAAK;;AAKjD,SAAgB,UAAU,KAAa;AACrC,KAAI;EACF,MAAM,YAAY,IAAI,IAAI,IAAI;AAG9B,SAAO,UAAU,WAAW,SAAS,OAAO,UAAU;SAChD;AACN,SAAO"}
|
package/package.json
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pecunia-root",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsdown",
|
|
11
|
+
"prepare": "",
|
|
12
|
+
"typecheck": "tsc --project tsconfig.json"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"main": "./dist/index.mjs",
|
|
18
|
+
"module": "./dist/index.mjs",
|
|
19
|
+
"types": "./dist/index.d.mts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"dev-source": "./src/index.ts",
|
|
23
|
+
"types": "./dist/index.d.mts",
|
|
24
|
+
"default": "./dist/index.mjs"
|
|
25
|
+
},
|
|
26
|
+
"./types": {
|
|
27
|
+
"dev-source": "./src/types/index.ts",
|
|
28
|
+
"types": "./dist/types/index.d.mts",
|
|
29
|
+
"default": "./dist/types/index.mjs"
|
|
30
|
+
},
|
|
31
|
+
"./crypto": {
|
|
32
|
+
"dev-source": "./src/crypto/index.ts",
|
|
33
|
+
"types": "./dist/crypto/index.d.mts",
|
|
34
|
+
"default": "./dist/crypto/index.mjs"
|
|
35
|
+
},
|
|
36
|
+
"./api": {
|
|
37
|
+
"dev-source": "./src/api/index.ts",
|
|
38
|
+
"types": "./dist/api/index.d.mts",
|
|
39
|
+
"default": "./dist/api/index.mjs"
|
|
40
|
+
},
|
|
41
|
+
"./db": {
|
|
42
|
+
"dev-source": "./src/db/index.ts",
|
|
43
|
+
"types": "./dist/db/index.d.mts",
|
|
44
|
+
"default": "./dist/db/index.mjs"
|
|
45
|
+
},
|
|
46
|
+
"./node": {
|
|
47
|
+
"dev-source": "./src/integrations/node.ts",
|
|
48
|
+
"types": "./dist/integrations/node.d.mts",
|
|
49
|
+
"default": "./dist/integrations/node.mjs"
|
|
50
|
+
},
|
|
51
|
+
"./adapters/prisma": {
|
|
52
|
+
"dev-source": "./src/adapters/prisma-adapter/index.ts",
|
|
53
|
+
"types": "./dist/adapters/prisma-adapter/index.d.mts",
|
|
54
|
+
"default": "./dist/adapters/prisma-adapter/index.mjs"
|
|
55
|
+
},
|
|
56
|
+
"./adapters/drizzle": {
|
|
57
|
+
"dev-source": "./src/adapters/drizzle-adapter/index.ts",
|
|
58
|
+
"types": "./dist/adapters/drizzle-adapter/index.d.mts",
|
|
59
|
+
"default": "./dist/adapters/drizzle-adapter/index.mjs"
|
|
60
|
+
},
|
|
61
|
+
"./adapters/mongodb": {
|
|
62
|
+
"dev-source": "./src/adapters/mongodb-adapter/index.ts",
|
|
63
|
+
"types": "./dist/adapters/mongodb-adapter/index.d.mts",
|
|
64
|
+
"default": "./dist/adapters/mongodb-adapter/index.mjs"
|
|
65
|
+
},
|
|
66
|
+
"./adapters": {
|
|
67
|
+
"dev-source": "./src/adapters/index.ts",
|
|
68
|
+
"types": "./dist/adapters/index.d.mts",
|
|
69
|
+
"default": "./dist/adapters/index.mjs"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"typesVersions": {
|
|
73
|
+
"*": {
|
|
74
|
+
"*": [
|
|
75
|
+
"./dist/index.d.mts"
|
|
76
|
+
],
|
|
77
|
+
"minimal": [
|
|
78
|
+
"./dist/auth/minimal.d.mts"
|
|
79
|
+
],
|
|
80
|
+
"node": [
|
|
81
|
+
"./dist/integrations/node.d.mts"
|
|
82
|
+
],
|
|
83
|
+
"types": [
|
|
84
|
+
"./dist/types/index.d.mts"
|
|
85
|
+
],
|
|
86
|
+
"crypto": [
|
|
87
|
+
"./dist/crypto/index.d.mts"
|
|
88
|
+
],
|
|
89
|
+
"api": [
|
|
90
|
+
"./dist/api/index.d.mts"
|
|
91
|
+
],
|
|
92
|
+
"db": [
|
|
93
|
+
"./dist/db/index.d.mts"
|
|
94
|
+
],
|
|
95
|
+
"adapters": [
|
|
96
|
+
"./dist/adapters/index.d.mts"
|
|
97
|
+
],
|
|
98
|
+
"adapters/prisma": [
|
|
99
|
+
"./dist/adapters/prisma-adapter/index.d.mts"
|
|
100
|
+
],
|
|
101
|
+
"adapters/drizzle": [
|
|
102
|
+
"./dist/adapters/drizzle-adapter/index.d.mts"
|
|
103
|
+
],
|
|
104
|
+
"adapters/mongodb": [
|
|
105
|
+
"./dist/adapters/mongodb-adapter/index.d.mts"
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"dependencies": {
|
|
110
|
+
"pecunia-core": "workspace:*",
|
|
111
|
+
"@better-auth/utils": "catalog:",
|
|
112
|
+
"@better-fetch/fetch": "catalog:",
|
|
113
|
+
"@noble/ciphers": "^2.0.0",
|
|
114
|
+
"@noble/hashes": "^2.0.0",
|
|
115
|
+
"better-call": "catalog:",
|
|
116
|
+
"defu": "^6.1.4",
|
|
117
|
+
"jose": "^6.1.0",
|
|
118
|
+
"kysely": "^0.28.5",
|
|
119
|
+
"nanostores": "^1.0.1",
|
|
120
|
+
"zod": "^4.1.12"
|
|
121
|
+
},
|
|
122
|
+
"devDependencies": {
|
|
123
|
+
"@prisma/client": "^5.22.0",
|
|
124
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
125
|
+
"@types/bun": "^1.3.3",
|
|
126
|
+
"@types/pg": "^8.15.5",
|
|
127
|
+
"better-sqlite3": "^12.4.1",
|
|
128
|
+
"deepmerge": "^4.3.1",
|
|
129
|
+
"drizzle-kit": "^0.31.4",
|
|
130
|
+
"drizzle-orm": "^0.41.0",
|
|
131
|
+
"happy-dom": "^20.0.10",
|
|
132
|
+
"listhen": "^1.9.0",
|
|
133
|
+
"mongodb": "^6.18.0",
|
|
134
|
+
"msw": "^2.12.4",
|
|
135
|
+
"mysql2": "^3.14.4",
|
|
136
|
+
"pg": "^8.16.3",
|
|
137
|
+
"prisma": "^5.22.0",
|
|
138
|
+
"tarn": "^3.0.2",
|
|
139
|
+
"tedious": "^18.6.1",
|
|
140
|
+
"tsdown": "catalog:",
|
|
141
|
+
"type-fest": "^5.2.0",
|
|
142
|
+
"typescript": "catalog:",
|
|
143
|
+
"vitest": "^4.0.15"
|
|
144
|
+
},
|
|
145
|
+
"peerDependencies": {
|
|
146
|
+
"@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
|
147
|
+
"better-sqlite3": "^12.0.0",
|
|
148
|
+
"drizzle-kit": ">=0.31.4",
|
|
149
|
+
"drizzle-orm": ">=0.41.0",
|
|
150
|
+
"mongodb": "^6.0.0 || ^7.0.0",
|
|
151
|
+
"mysql2": "^3.0.0",
|
|
152
|
+
"next": "^14.0.0 || ^15.0.0 || ^16.0.0",
|
|
153
|
+
"pg": "^8.0.0",
|
|
154
|
+
"prisma": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
|
155
|
+
"vitest": "^2.0.0 || ^3.0.0 || ^4.0.0"
|
|
156
|
+
},
|
|
157
|
+
"peerDependenciesMeta": {
|
|
158
|
+
"drizzle-kit": {
|
|
159
|
+
"optional": true
|
|
160
|
+
},
|
|
161
|
+
"drizzle-orm": {
|
|
162
|
+
"optional": true
|
|
163
|
+
},
|
|
164
|
+
"mongodb": {
|
|
165
|
+
"optional": true
|
|
166
|
+
},
|
|
167
|
+
"mysql2": {
|
|
168
|
+
"optional": true
|
|
169
|
+
},
|
|
170
|
+
"pg": {
|
|
171
|
+
"optional": true
|
|
172
|
+
},
|
|
173
|
+
"prisma": {
|
|
174
|
+
"optional": true
|
|
175
|
+
},
|
|
176
|
+
"better-sqlite3": {
|
|
177
|
+
"optional": true
|
|
178
|
+
},
|
|
179
|
+
"vitest": {
|
|
180
|
+
"optional": true
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|