@kitledger/core 0.0.2 → 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/accounts.d.ts +44 -0
- package/dist/accounts.js +221 -0
- package/dist/auth.d.ts +34 -0
- package/dist/auth.js +24 -0
- package/dist/config.d.ts +28 -0
- package/dist/config.js +48 -0
- package/dist/crypto.d.ts +2 -0
- package/dist/crypto.js +13 -0
- package/dist/db.d.ts +78 -0
- package/dist/db.js +86 -0
- package/dist/factories.d.ts +45 -0
- package/dist/factories.js +150 -0
- package/dist/fields.d.ts +71 -0
- package/dist/fields.js +13 -0
- package/dist/index.d.ts +1 -1
- package/dist/jwt.d.ts +10 -0
- package/dist/jwt.js +66 -0
- package/dist/ledgers.d.ts +22 -0
- package/dist/ledgers.js +144 -0
- package/dist/query.d.ts +19 -0
- package/dist/query.js +217 -0
- package/dist/schema.d.ts +1190 -0
- package/dist/schema.js +159 -0
- package/dist/transactions.d.ts +22 -2
- package/dist/transactions.js +5 -1
- package/dist/users.d.ts +16 -0
- package/dist/users.js +198 -0
- package/dist/validation.d.ts +22 -0
- package/dist/validation.js +10 -0
- package/package.json +22 -8
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { InferInsertModel, InferSelectModel } from "drizzle-orm";
|
|
2
|
+
import * as v from "valibot";
|
|
3
|
+
import { InferOutput } from "valibot";
|
|
4
|
+
import { KitledgerDb } from "./db.js";
|
|
5
|
+
import { FilterOperationParameters, GetOperationResult } from "./db.js";
|
|
6
|
+
import { accounts } from "./schema.js";
|
|
7
|
+
import { ValidationFailure, ValidationSuccess } from "./validation.js";
|
|
8
|
+
export declare enum BalanceType {
|
|
9
|
+
DEBIT = "debit",
|
|
10
|
+
CREDIT = "credit"
|
|
11
|
+
}
|
|
12
|
+
export declare const AccountCreateSchema: v.ObjectSchema<{
|
|
13
|
+
readonly ref_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MaxLengthAction<string, 64, undefined>]>;
|
|
14
|
+
readonly alt_id: v.NullishSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MaxLengthAction<string, 64, undefined>]>, null>;
|
|
15
|
+
readonly balance_type: v.EnumSchema<typeof BalanceType, undefined>;
|
|
16
|
+
readonly ledger_id: v.StringSchema<undefined>;
|
|
17
|
+
readonly parent_id: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
18
|
+
readonly name: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.NonEmptyAction<string, undefined>, v.MaxLengthAction<string, 64, undefined>]>;
|
|
19
|
+
readonly meta: v.NullishSchema<v.RecordSchema<v.StringSchema<undefined>, v.UnionSchema<[v.StringSchema<undefined>, v.NumberSchema<undefined>, v.DateSchema<undefined>, v.BooleanSchema<undefined>, v.NullSchema<undefined>], undefined>, undefined>, null>;
|
|
20
|
+
readonly active: v.NullishSchema<v.BooleanSchema<undefined>, true>;
|
|
21
|
+
readonly created_at: v.OptionalSchema<v.DateSchema<undefined>, undefined>;
|
|
22
|
+
readonly updated_at: v.OptionalSchema<v.NullableSchema<v.DateSchema<undefined>, undefined>, undefined>;
|
|
23
|
+
}, undefined>;
|
|
24
|
+
export type AccountInsert = InferInsertModel<typeof accounts>;
|
|
25
|
+
export type Account = InferSelectModel<typeof accounts>;
|
|
26
|
+
export type AccountCreateData = InferOutput<typeof AccountCreateSchema>;
|
|
27
|
+
/**
|
|
28
|
+
* Procedurally builds and executes a filter query for accounts based on the provided parameters.
|
|
29
|
+
* @param params
|
|
30
|
+
* @returns
|
|
31
|
+
*/
|
|
32
|
+
export declare function filterAccounts(db: KitledgerDb, params: FilterOperationParameters): Promise<GetOperationResult<Account>>;
|
|
33
|
+
/**
|
|
34
|
+
* Finds the parent account by ID or ref_id or alt_id.
|
|
35
|
+
* Returns the ID of the parent account if found, otherwise returns null.
|
|
36
|
+
*/
|
|
37
|
+
export declare function findParentAccount(db: KitledgerDb, parentId: string, ledgerId?: string | null): Promise<Account | null>;
|
|
38
|
+
/**
|
|
39
|
+
* Finds the ledger by ID or ref_id or alt_id.
|
|
40
|
+
* @param ledgerId
|
|
41
|
+
* @returns
|
|
42
|
+
*/
|
|
43
|
+
export declare function findLedgerId(db: KitledgerDb, ledgerId: string): Promise<string | null>;
|
|
44
|
+
export declare function createAccount(db: KitledgerDb, data: AccountCreateData): Promise<ValidationSuccess<Account> | ValidationFailure<AccountCreateData>>;
|
package/dist/accounts.js
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { and, eq, or, sql } from "drizzle-orm";
|
|
2
|
+
import { v7 } from "uuid";
|
|
3
|
+
import * as v from "valibot";
|
|
4
|
+
import { ANY, defaultLimit, defaultOffset, maxLimit, parseBooleanFilterValue, } from "./db.js";
|
|
5
|
+
import { accounts, ledgers } from "./schema.js";
|
|
6
|
+
import { parseValibotIssues, } from "./validation.js";
|
|
7
|
+
export var BalanceType;
|
|
8
|
+
(function (BalanceType) {
|
|
9
|
+
BalanceType["DEBIT"] = "debit";
|
|
10
|
+
BalanceType["CREDIT"] = "credit";
|
|
11
|
+
})(BalanceType || (BalanceType = {}));
|
|
12
|
+
export const AccountCreateSchema = v.object({
|
|
13
|
+
ref_id: v.pipe(v.string(), v.maxLength(64)),
|
|
14
|
+
alt_id: v.nullish(v.pipe(v.string(), v.maxLength(64)), null),
|
|
15
|
+
balance_type: v.enum(BalanceType),
|
|
16
|
+
ledger_id: v.string(),
|
|
17
|
+
parent_id: v.optional(v.nullable(v.string())),
|
|
18
|
+
name: v.pipe(v.string(), v.nonEmpty(), v.maxLength(64)),
|
|
19
|
+
meta: v.nullish(v.record(v.string(), v.union([v.string(), v.number(), v.date(), v.boolean(), v.null()])), null),
|
|
20
|
+
active: v.nullish(v.boolean(), true),
|
|
21
|
+
created_at: v.optional(v.date()),
|
|
22
|
+
updated_at: v.optional(v.nullable(v.date())),
|
|
23
|
+
});
|
|
24
|
+
/**
|
|
25
|
+
* Procedurally builds and executes a filter query for accounts based on the provided parameters.
|
|
26
|
+
* @param params
|
|
27
|
+
* @returns
|
|
28
|
+
*/
|
|
29
|
+
export async function filterAccounts(db, params) {
|
|
30
|
+
const { limit = defaultLimit, offset = defaultOffset, ...filters } = params;
|
|
31
|
+
const filterConditions = [];
|
|
32
|
+
let ledgerId = null;
|
|
33
|
+
for (const [key, value] of Object.entries(filters)) {
|
|
34
|
+
if (key === accounts.id.name && String(value).length > 0) {
|
|
35
|
+
filterConditions.push(eq(accounts.id, String(value)));
|
|
36
|
+
}
|
|
37
|
+
if (key === accounts.ref_id.name && String(value).length > 0) {
|
|
38
|
+
filterConditions.push(eq(accounts.ref_id, String(value)));
|
|
39
|
+
}
|
|
40
|
+
if (key === accounts.alt_id.name && String(value).length > 0) {
|
|
41
|
+
filterConditions.push(eq(accounts.alt_id, String(value)));
|
|
42
|
+
}
|
|
43
|
+
if (key === accounts.balance_type.name && String(value).length > 0) {
|
|
44
|
+
filterConditions.push(eq(accounts.balance_type, String(value)));
|
|
45
|
+
}
|
|
46
|
+
if (key === accounts.name.name && String(value).length > 0) {
|
|
47
|
+
filterConditions.push(sql `${accounts.name} ILIKE ${"%" + String(value) + "%"}`);
|
|
48
|
+
}
|
|
49
|
+
if (key === accounts.ledger_id.name && String(value).length > 0) {
|
|
50
|
+
ledgerId = await findLedgerId(db, String(value));
|
|
51
|
+
if (ledgerId) {
|
|
52
|
+
filterConditions.push(eq(accounts.ledger_id, ledgerId));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (key === accounts.active.name && String(value).length > 0) {
|
|
56
|
+
if (value === ANY) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const booleanValue = parseBooleanFilterValue(String(value));
|
|
60
|
+
if (booleanValue !== null) {
|
|
61
|
+
filterConditions.push(eq(accounts.active, booleanValue));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (key === accounts.parent_id.name && String(value).length > 0) {
|
|
65
|
+
const parentAccount = await findParentAccount(db, String(value), null);
|
|
66
|
+
if (parentAccount) {
|
|
67
|
+
filterConditions.push(eq(accounts.parent_id, parentAccount.id));
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
filterConditions.push(eq(sql `${accounts.parent_id}::text`, value));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Default to only active accounts if no active filter is provided
|
|
75
|
+
if (!Object.keys(filters).includes(accounts.active.name)) {
|
|
76
|
+
filterConditions.push(eq(accounts.active, true));
|
|
77
|
+
}
|
|
78
|
+
const results = await db
|
|
79
|
+
.select()
|
|
80
|
+
.from(accounts)
|
|
81
|
+
.where(and(...filterConditions))
|
|
82
|
+
.limit(Math.min(limit, maxLimit))
|
|
83
|
+
.offset(offset);
|
|
84
|
+
return {
|
|
85
|
+
data: results,
|
|
86
|
+
limit: Math.min(limit, maxLimit),
|
|
87
|
+
offset: offset,
|
|
88
|
+
count: results.length,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Finds the parent account by ID or ref_id or alt_id.
|
|
93
|
+
* Returns the ID of the parent account if found, otherwise returns null.
|
|
94
|
+
*/
|
|
95
|
+
export async function findParentAccount(db, parentId, ledgerId) {
|
|
96
|
+
if (!parentId) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const parent = await db.query.accounts.findFirst({
|
|
100
|
+
where: and(or(eq(sql `${accounts.id}::text`, parentId), eq(accounts.ref_id, parentId), eq(accounts.alt_id, parentId)), ledgerId ? eq(accounts.ledger_id, ledgerId) : undefined, eq(accounts.active, true)),
|
|
101
|
+
});
|
|
102
|
+
return parent ? parent : null;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Finds the ledger by ID or ref_id or alt_id.
|
|
106
|
+
* @param ledgerId
|
|
107
|
+
* @returns
|
|
108
|
+
*/
|
|
109
|
+
export async function findLedgerId(db, ledgerId) {
|
|
110
|
+
const ledger = await db.query.ledgers.findFirst({
|
|
111
|
+
where: and(or(eq(sql `${ledgers.id}::text`, ledgerId), eq(ledgers.ref_id, ledgerId), eq(ledgers.alt_id, ledgerId)), eq(ledgers.active, true)),
|
|
112
|
+
columns: { id: true },
|
|
113
|
+
});
|
|
114
|
+
return ledger ? ledger.id : null;
|
|
115
|
+
}
|
|
116
|
+
// ACTIONS
|
|
117
|
+
async function refIdAlreadyExists(db, refId) {
|
|
118
|
+
const results = await db.query.accounts.findMany({
|
|
119
|
+
where: eq(accounts.ref_id, refId),
|
|
120
|
+
columns: { id: true },
|
|
121
|
+
});
|
|
122
|
+
return results.length > 0;
|
|
123
|
+
}
|
|
124
|
+
async function altIdAlreadyExists(db, altId) {
|
|
125
|
+
if (!altId) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
const results = await db.query.accounts.findMany({
|
|
129
|
+
where: eq(accounts.alt_id, altId),
|
|
130
|
+
columns: { id: true },
|
|
131
|
+
});
|
|
132
|
+
return results.length > 0;
|
|
133
|
+
}
|
|
134
|
+
async function validateAccountCreate(db, data) {
|
|
135
|
+
const result = v.safeParse(AccountCreateSchema, data);
|
|
136
|
+
let success = result.success;
|
|
137
|
+
if (!result.success) {
|
|
138
|
+
return { success: false, errors: parseValibotIssues(result.issues) };
|
|
139
|
+
}
|
|
140
|
+
const errors = [];
|
|
141
|
+
const [refIdError, altIdError, ledgerId] = await Promise.all([
|
|
142
|
+
refIdAlreadyExists(db, result.output.ref_id),
|
|
143
|
+
altIdAlreadyExists(db, result.output.alt_id ?? null),
|
|
144
|
+
findLedgerId(db, result.output.ledger_id),
|
|
145
|
+
]);
|
|
146
|
+
if (refIdError) {
|
|
147
|
+
success = false;
|
|
148
|
+
errors.push({
|
|
149
|
+
type: "data",
|
|
150
|
+
path: "ref_id",
|
|
151
|
+
message: "Ref ID already exists.",
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
if (altIdError) {
|
|
155
|
+
success = false;
|
|
156
|
+
errors.push({
|
|
157
|
+
type: "data",
|
|
158
|
+
path: "alt_id",
|
|
159
|
+
message: "Alt ID already exists.",
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
if (ledgerId) {
|
|
163
|
+
result.output.ledger_id = ledgerId;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
success = false;
|
|
167
|
+
errors.push({
|
|
168
|
+
type: "data",
|
|
169
|
+
path: "ledger_id",
|
|
170
|
+
message: "Invalid ledger ID.",
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
if (result.output.parent_id) {
|
|
174
|
+
const parentAccount = await findParentAccount(db, result.output.parent_id, result.output.ledger_id);
|
|
175
|
+
if (parentAccount) {
|
|
176
|
+
result.output.parent_id = result.output.parent_id ? parentAccount.id : null;
|
|
177
|
+
result.output.balance_type = parentAccount.balance_type;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
success = false;
|
|
181
|
+
errors.push({
|
|
182
|
+
type: "data",
|
|
183
|
+
path: "parent_id",
|
|
184
|
+
message: "Invalid parent account ID.",
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return { success, data: result.output, errors: errors };
|
|
189
|
+
}
|
|
190
|
+
export async function createAccount(db, data) {
|
|
191
|
+
const validation = await validateAccountCreate(db, data);
|
|
192
|
+
if (!validation.success || !validation.data) {
|
|
193
|
+
return {
|
|
194
|
+
success: false,
|
|
195
|
+
data: data,
|
|
196
|
+
errors: validation.errors,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
const insertData = {
|
|
200
|
+
id: v7(),
|
|
201
|
+
...validation.data,
|
|
202
|
+
};
|
|
203
|
+
const result = await db.insert(accounts).values(insertData).returning();
|
|
204
|
+
if (result.length === 0) {
|
|
205
|
+
return {
|
|
206
|
+
success: false,
|
|
207
|
+
data: validation.data,
|
|
208
|
+
errors: [
|
|
209
|
+
{
|
|
210
|
+
type: "data",
|
|
211
|
+
path: null,
|
|
212
|
+
message: "Failed to create account.",
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
success: true,
|
|
219
|
+
data: result[0],
|
|
220
|
+
};
|
|
221
|
+
}
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { InferInsertModel, InferSelectModel } from "drizzle-orm";
|
|
2
|
+
import { type KitledgerDb } from "./db.js";
|
|
3
|
+
import { api_tokens, permission_assignments, permissions, roles, system_permissions, user_roles, users } from "./schema.js";
|
|
4
|
+
export type ApiTokenInsert = InferInsertModel<typeof api_tokens>;
|
|
5
|
+
export type ApiToken = InferSelectModel<typeof api_tokens>;
|
|
6
|
+
export type PermissionAssignmentInsert = InferInsertModel<typeof permission_assignments>;
|
|
7
|
+
export type PermissionAssignment = InferSelectModel<typeof permission_assignments>;
|
|
8
|
+
export type PermissionInsert = InferInsertModel<typeof permissions>;
|
|
9
|
+
export type Permission = InferSelectModel<typeof permissions>;
|
|
10
|
+
export type RoleInsert = InferInsertModel<typeof roles>;
|
|
11
|
+
export type Role = InferSelectModel<typeof roles>;
|
|
12
|
+
export type SystemPermissionInsert = InferInsertModel<typeof system_permissions>;
|
|
13
|
+
export type SystemPermission = InferSelectModel<typeof system_permissions>;
|
|
14
|
+
export type UserInsert = InferInsertModel<typeof users>;
|
|
15
|
+
export type User = InferSelectModel<typeof users>;
|
|
16
|
+
export type UserRoleInsert = InferInsertModel<typeof user_roles>;
|
|
17
|
+
export type UserRole = InferSelectModel<typeof user_roles>;
|
|
18
|
+
export type AppUser = Omit<User, "password_hash"> & {
|
|
19
|
+
roles: Role[];
|
|
20
|
+
permissions: Permission[];
|
|
21
|
+
system_permissions: SystemPermission[];
|
|
22
|
+
};
|
|
23
|
+
export declare const SYSTEM_ADMIN_PERMISSION = "KL_SYSTEM_ADMIN";
|
|
24
|
+
export declare function startSession(db: KitledgerDb, userId: string, sessionConfig: SessionConfigOptions): Promise<string>;
|
|
25
|
+
export declare function createToken(db: KitledgerDb, userId: string, name?: string | null): Promise<string>;
|
|
26
|
+
export type AuthConfigOptions = {
|
|
27
|
+
secret: string;
|
|
28
|
+
pastSecrets: string[];
|
|
29
|
+
jwtAlgorithm: "HS256";
|
|
30
|
+
};
|
|
31
|
+
export type SessionConfigOptions = {
|
|
32
|
+
cookieName: string;
|
|
33
|
+
ttl: number;
|
|
34
|
+
};
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { v7 } from "uuid";
|
|
2
|
+
import { api_tokens, sessions, } from "./schema.js";
|
|
3
|
+
const SYSTEM_PERMISSION_PREFIX = "KL_";
|
|
4
|
+
export const SYSTEM_ADMIN_PERMISSION = `${SYSTEM_PERMISSION_PREFIX}SYSTEM_ADMIN`;
|
|
5
|
+
export async function startSession(db, userId, sessionConfig) {
|
|
6
|
+
const sessionId = v7();
|
|
7
|
+
await db.insert(sessions).values({
|
|
8
|
+
id: sessionId,
|
|
9
|
+
user_id: userId,
|
|
10
|
+
expires_at: new Date(Date.now() + sessionConfig.ttl * 1000),
|
|
11
|
+
created_at: new Date(),
|
|
12
|
+
});
|
|
13
|
+
return sessionId;
|
|
14
|
+
}
|
|
15
|
+
export async function createToken(db, userId, name) {
|
|
16
|
+
const tokenId = v7();
|
|
17
|
+
await db.insert(api_tokens).values({
|
|
18
|
+
id: tokenId,
|
|
19
|
+
user_id: userId,
|
|
20
|
+
name: name ?? "API Token",
|
|
21
|
+
revoked_at: null,
|
|
22
|
+
});
|
|
23
|
+
return tokenId;
|
|
24
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
type CorsConfig = {
|
|
2
|
+
origin: string | string[];
|
|
3
|
+
allowMethods?: string[];
|
|
4
|
+
allowHeaders?: string[];
|
|
5
|
+
maxAge?: number;
|
|
6
|
+
credentials?: boolean;
|
|
7
|
+
exposeHeaders?: string[];
|
|
8
|
+
};
|
|
9
|
+
type ServerConfig = {
|
|
10
|
+
port: number;
|
|
11
|
+
cors: CorsConfig;
|
|
12
|
+
};
|
|
13
|
+
type WorkerConfig = {
|
|
14
|
+
poolSize: number;
|
|
15
|
+
taskTimeout: number;
|
|
16
|
+
maxQueueSize: number;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Export pre-assembled configuration values for the HTTP server.
|
|
20
|
+
* Values are a mix of environment variables and defaults.
|
|
21
|
+
*/
|
|
22
|
+
export declare const serverConfig: ServerConfig;
|
|
23
|
+
/**
|
|
24
|
+
* Export pre-assembled configuration values for the worker pool.
|
|
25
|
+
* Values are a mix of environment variables and defaults.
|
|
26
|
+
*/
|
|
27
|
+
export declare const workerConfig: WorkerConfig;
|
|
28
|
+
export {};
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { cpus } from "node:os";
|
|
2
|
+
/**
|
|
3
|
+
* CORS configuration values and defaults.
|
|
4
|
+
*/
|
|
5
|
+
const corsDefaultHeaders = ["Content-Type", "Authorization", "X-Requested-With"];
|
|
6
|
+
const corsAllowedHeaders = [
|
|
7
|
+
...new Set([...corsDefaultHeaders, ...(process.env.KL_CORS_ALLOWED_HEADERS?.split(",") || [])]),
|
|
8
|
+
];
|
|
9
|
+
const corsAllowedMethods = ["GET", "POST", "PUT", "DELETE", "OPTIONS"];
|
|
10
|
+
const corsAllowedOrigins = process.env.KL_CORS_ALLOWED_ORIGINS ? process.env.KL_CORS_ALLOWED_ORIGINS.split(",") : "*";
|
|
11
|
+
const corsCredentials = false;
|
|
12
|
+
const corsExposeHeaders = [];
|
|
13
|
+
const corsMaxAge = parseInt(process.env.KL_CORS_MAX_AGE || "86400");
|
|
14
|
+
/**
|
|
15
|
+
* Worker pool configuration values and defaults.
|
|
16
|
+
*/
|
|
17
|
+
const workerPoolSize = parseInt(process.env.KL_WORKER_POOL_SIZE || String(cpus().length)) || 1;
|
|
18
|
+
const workerTaskTimeout = parseInt(process.env.KL_WORKER_TASK_TIMEOUT || "5000"); // Default to 5 seconds
|
|
19
|
+
const workerMaxQueueSize = process.env.KL_WORKER_MAX_QUEUE_SIZE
|
|
20
|
+
? parseInt(process.env.KL_WORKER_MAX_QUEUE_SIZE)
|
|
21
|
+
: Infinity;
|
|
22
|
+
/*
|
|
23
|
+
* 3) Export the configuration objects.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Export pre-assembled configuration values for the HTTP server.
|
|
27
|
+
* Values are a mix of environment variables and defaults.
|
|
28
|
+
*/
|
|
29
|
+
export const serverConfig = {
|
|
30
|
+
port: process.env.KL_SERVER_PORT ? parseInt(process.env.KL_SERVER_PORT) : 8888,
|
|
31
|
+
cors: {
|
|
32
|
+
origin: corsAllowedOrigins,
|
|
33
|
+
allowMethods: corsAllowedMethods,
|
|
34
|
+
allowHeaders: corsAllowedHeaders,
|
|
35
|
+
exposeHeaders: corsExposeHeaders,
|
|
36
|
+
credentials: corsCredentials,
|
|
37
|
+
maxAge: corsMaxAge,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Export pre-assembled configuration values for the worker pool.
|
|
42
|
+
* Values are a mix of environment variables and defaults.
|
|
43
|
+
*/
|
|
44
|
+
export const workerConfig = {
|
|
45
|
+
poolSize: workerPoolSize,
|
|
46
|
+
taskTimeout: workerTaskTimeout,
|
|
47
|
+
maxQueueSize: workerMaxQueueSize,
|
|
48
|
+
};
|
package/dist/crypto.d.ts
ADDED
package/dist/crypto.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { hash, verify } from "@node-rs/argon2";
|
|
2
|
+
export function hashPassword(input) {
|
|
3
|
+
const hashOptions = {
|
|
4
|
+
type: 2, // argon2id
|
|
5
|
+
memoryCost: 65536, // 64 MiB
|
|
6
|
+
timeCost: 5, // 5 iterations
|
|
7
|
+
parallelism: 1,
|
|
8
|
+
};
|
|
9
|
+
return hash(input, hashOptions);
|
|
10
|
+
}
|
|
11
|
+
export function verifyPassword(hashStr, input) {
|
|
12
|
+
return verify(hashStr, input);
|
|
13
|
+
}
|
package/dist/db.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
|
2
|
+
import postgres from "postgres";
|
|
3
|
+
import * as v from "valibot";
|
|
4
|
+
import * as schema from "./schema.js";
|
|
5
|
+
type DbOptions = {
|
|
6
|
+
url: string;
|
|
7
|
+
ssl?: boolean;
|
|
8
|
+
max?: number;
|
|
9
|
+
migrations_table?: string;
|
|
10
|
+
migrations_schema?: string;
|
|
11
|
+
};
|
|
12
|
+
export type KitledgerDb = PostgresJsDatabase<typeof schema> & {
|
|
13
|
+
$client: postgres.Sql<{}>;
|
|
14
|
+
};
|
|
15
|
+
export declare function initializeDatabase(options: DbOptions): Promise<PostgresJsDatabase<typeof schema> & {
|
|
16
|
+
$client: postgres.Sql<{}>;
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* Common database helper for timestamps
|
|
20
|
+
*/
|
|
21
|
+
export declare const timestamps: {
|
|
22
|
+
created_at: import("drizzle-orm").NotNull<import("drizzle-orm").HasDefault<import("drizzle-orm/pg-core").PgTimestampBuilderInitial<"created_at">>>;
|
|
23
|
+
updated_at: import("drizzle-orm/pg-core").PgTimestampBuilderInitial<"updated_at">;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Supported operation types for get operations.
|
|
27
|
+
*/
|
|
28
|
+
export declare enum GetOperationType {
|
|
29
|
+
FILTER = "filter",
|
|
30
|
+
SEARCH = "search",
|
|
31
|
+
QUERY = "query"
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Parameters for filter operations, including optional limit and offset.
|
|
35
|
+
*/
|
|
36
|
+
export type FilterOperationParameters = Record<string, string> & {
|
|
37
|
+
limit?: number;
|
|
38
|
+
offset?: number;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Result structure for get operations, including data array and optional limit and offset.
|
|
42
|
+
*/
|
|
43
|
+
export type GetOperationResult<T> = {
|
|
44
|
+
data: T[];
|
|
45
|
+
count: number;
|
|
46
|
+
limit?: number;
|
|
47
|
+
offset?: number;
|
|
48
|
+
errors?: {
|
|
49
|
+
field?: string;
|
|
50
|
+
message: string;
|
|
51
|
+
}[];
|
|
52
|
+
};
|
|
53
|
+
export type QueryResultRow = v.InferInput<typeof QueryResultRowSchema>;
|
|
54
|
+
export declare const QueryResultRowSchema: v.RecordSchema<v.StringSchema<undefined>, v.UnionSchema<[v.StringSchema<undefined>, v.NumberSchema<undefined>, v.BooleanSchema<undefined>, v.DateSchema<undefined>, v.NullSchema<undefined>, v.RecordSchema<v.StringSchema<undefined>, v.UnionSchema<[v.StringSchema<undefined>, v.NumberSchema<undefined>, v.BooleanSchema<undefined>, v.DateSchema<undefined>, v.NullSchema<undefined>], undefined>, undefined>], undefined>, undefined>;
|
|
55
|
+
export declare const QueryResultSchema: v.ArraySchema<v.RecordSchema<v.StringSchema<undefined>, v.UnionSchema<[v.StringSchema<undefined>, v.NumberSchema<undefined>, v.BooleanSchema<undefined>, v.DateSchema<undefined>, v.NullSchema<undefined>, v.RecordSchema<v.StringSchema<undefined>, v.UnionSchema<[v.StringSchema<undefined>, v.NumberSchema<undefined>, v.BooleanSchema<undefined>, v.DateSchema<undefined>, v.NullSchema<undefined>], undefined>, undefined>], undefined>, undefined>, undefined>;
|
|
56
|
+
/**
|
|
57
|
+
* Utility function to parse boolean filter values from strings or booleans to be used in queries and filters.
|
|
58
|
+
* @param value
|
|
59
|
+
* @returns
|
|
60
|
+
*/
|
|
61
|
+
export declare function parseBooleanFilterValue(value: string | boolean): boolean | null;
|
|
62
|
+
/**
|
|
63
|
+
* String constant representing a wildcard for boolean filters.
|
|
64
|
+
*/
|
|
65
|
+
export declare const ANY = "any";
|
|
66
|
+
/**
|
|
67
|
+
* Default value for pagination limit.
|
|
68
|
+
*/
|
|
69
|
+
export declare const defaultLimit = 100;
|
|
70
|
+
/**
|
|
71
|
+
* Maximum allowable value for pagination limit.
|
|
72
|
+
*/
|
|
73
|
+
export declare const maxLimit = 1000;
|
|
74
|
+
/**
|
|
75
|
+
* Default value for pagination offset.
|
|
76
|
+
*/
|
|
77
|
+
export declare const defaultOffset = 0;
|
|
78
|
+
export {};
|
package/dist/db.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { timestamp } from "drizzle-orm/pg-core";
|
|
2
|
+
import { drizzle } from "drizzle-orm/postgres-js";
|
|
3
|
+
import { migrate } from "drizzle-orm/postgres-js/migrator";
|
|
4
|
+
import * as v from "valibot";
|
|
5
|
+
import * as schema from "./schema.js";
|
|
6
|
+
async function runMigrations(db, migrationsTable, migrationsSchema) {
|
|
7
|
+
await migrate(db, {
|
|
8
|
+
migrationsFolder: "./migrations",
|
|
9
|
+
migrationsTable: migrationsTable,
|
|
10
|
+
migrationsSchema: migrationsSchema,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export async function initializeDatabase(options) {
|
|
14
|
+
const dbConfig = {
|
|
15
|
+
url: options.url,
|
|
16
|
+
ssl: options.ssl ? options.ssl : false,
|
|
17
|
+
max: options.max ? options.max : 10,
|
|
18
|
+
};
|
|
19
|
+
const db = drizzle({
|
|
20
|
+
connection: dbConfig,
|
|
21
|
+
schema: schema,
|
|
22
|
+
});
|
|
23
|
+
const migrationsTable = options.migrations_table || "schema_history";
|
|
24
|
+
const migrationsSchema = options.migrations_schema || "public";
|
|
25
|
+
await runMigrations(db, migrationsTable, migrationsSchema);
|
|
26
|
+
return db;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Common database helper for timestamps
|
|
30
|
+
*/
|
|
31
|
+
export const timestamps = {
|
|
32
|
+
created_at: timestamp("created_at", { mode: "date" }).defaultNow().notNull(),
|
|
33
|
+
updated_at: timestamp("updated_at", { mode: "date" }),
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Supported operation types for get operations.
|
|
37
|
+
*/
|
|
38
|
+
export var GetOperationType;
|
|
39
|
+
(function (GetOperationType) {
|
|
40
|
+
GetOperationType["FILTER"] = "filter";
|
|
41
|
+
GetOperationType["SEARCH"] = "search";
|
|
42
|
+
GetOperationType["QUERY"] = "query";
|
|
43
|
+
})(GetOperationType || (GetOperationType = {}));
|
|
44
|
+
export const QueryResultRowSchema = v.record(v.string(), v.union([
|
|
45
|
+
v.string(),
|
|
46
|
+
v.number(),
|
|
47
|
+
v.boolean(),
|
|
48
|
+
v.date(),
|
|
49
|
+
v.null(),
|
|
50
|
+
v.record(v.string(), v.union([v.string(), v.number(), v.boolean(), v.date(), v.null()])),
|
|
51
|
+
]));
|
|
52
|
+
export const QueryResultSchema = v.array(QueryResultRowSchema);
|
|
53
|
+
/**
|
|
54
|
+
* Utility function to parse boolean filter values from strings or booleans to be used in queries and filters.
|
|
55
|
+
* @param value
|
|
56
|
+
* @returns
|
|
57
|
+
*/
|
|
58
|
+
export function parseBooleanFilterValue(value) {
|
|
59
|
+
if (typeof value === "boolean") {
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
const lowerValue = value.toLowerCase();
|
|
63
|
+
if (lowerValue === "true") {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
if (lowerValue === "false") {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* String constant representing a wildcard for boolean filters.
|
|
73
|
+
*/
|
|
74
|
+
export const ANY = "any";
|
|
75
|
+
/**
|
|
76
|
+
* Default value for pagination limit.
|
|
77
|
+
*/
|
|
78
|
+
export const defaultLimit = 100;
|
|
79
|
+
/**
|
|
80
|
+
* Maximum allowable value for pagination limit.
|
|
81
|
+
*/
|
|
82
|
+
export const maxLimit = 1000;
|
|
83
|
+
/**
|
|
84
|
+
* Default value for pagination offset.
|
|
85
|
+
*/
|
|
86
|
+
export const defaultOffset = 0;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Account } from "./accounts.js";
|
|
2
|
+
import { ApiToken, Permission, PermissionAssignment, Role, SystemPermission, User, UserRole } from "./auth.js";
|
|
3
|
+
import { Ledger } from "./ledgers.js";
|
|
4
|
+
/**
|
|
5
|
+
* A generic factory function to create an array of items.
|
|
6
|
+
* @param factory A function that creates a single item.
|
|
7
|
+
* @param count The number of items to create.
|
|
8
|
+
* @returns An array of created items.
|
|
9
|
+
*/
|
|
10
|
+
export declare const factory: <T>(factory: () => T, count: number) => T[];
|
|
11
|
+
export declare class BaseFactory<T> {
|
|
12
|
+
private factory;
|
|
13
|
+
constructor(factory: () => T);
|
|
14
|
+
make(count: number): T[];
|
|
15
|
+
}
|
|
16
|
+
export declare class ApiTokenFactory extends BaseFactory<ApiToken> {
|
|
17
|
+
constructor();
|
|
18
|
+
}
|
|
19
|
+
export declare class PermissionFactory extends BaseFactory<Permission> {
|
|
20
|
+
constructor();
|
|
21
|
+
}
|
|
22
|
+
export declare class PermissionAssignmentFactory extends BaseFactory<PermissionAssignment> {
|
|
23
|
+
constructor();
|
|
24
|
+
}
|
|
25
|
+
export declare class RoleFactory extends BaseFactory<Role> {
|
|
26
|
+
constructor();
|
|
27
|
+
}
|
|
28
|
+
export declare class SessionFactory extends BaseFactory<string> {
|
|
29
|
+
constructor();
|
|
30
|
+
}
|
|
31
|
+
export declare class SystemPermissionFactory extends BaseFactory<SystemPermission> {
|
|
32
|
+
constructor();
|
|
33
|
+
}
|
|
34
|
+
export declare class UserFactory extends BaseFactory<User> {
|
|
35
|
+
constructor();
|
|
36
|
+
}
|
|
37
|
+
export declare class UserRoleFactory extends BaseFactory<UserRole> {
|
|
38
|
+
constructor();
|
|
39
|
+
}
|
|
40
|
+
export declare class LedgerFactory extends BaseFactory<Ledger> {
|
|
41
|
+
constructor();
|
|
42
|
+
}
|
|
43
|
+
export declare class AccountFactory extends BaseFactory<Account> {
|
|
44
|
+
constructor();
|
|
45
|
+
}
|