yamchart 0.3.1 → 0.3.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/chunk-4P5UHWYK.js +247 -0
- package/dist/chunk-4P5UHWYK.js.map +1 -0
- package/dist/chunk-A24KVXJQ.js +922 -0
- package/dist/chunk-A24KVXJQ.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/chunk-LL3VKMZM.js +1265 -0
- package/dist/chunk-LL3VKMZM.js.map +1 -0
- package/dist/dev-PTVNJI7G.js +13557 -0
- package/dist/dev-PTVNJI7G.js.map +1 -0
- package/dist/dist-JH7OL7U4.js +16 -0
- package/dist/dist-JH7OL7U4.js.map +1 -0
- package/dist/dist-JKJLH3F6.js +56 -0
- package/dist/dist-JKJLH3F6.js.map +1 -0
- package/dist/{generate-RD3LCS73.js → generate-KNER36CB.js} +3 -1
- package/dist/{generate-RD3LCS73.js.map → generate-KNER36CB.js.map} +1 -1
- package/dist/index.js +13 -11
- package/dist/index.js.map +1 -1
- package/dist/{init-6D5VNGSP.js → init-FTSEOTAD.js} +3 -1
- package/dist/{init-6D5VNGSP.js.map → init-FTSEOTAD.js.map} +1 -1
- package/dist/{reset-password-MJ54ICGP.js → reset-password-IZQTDTU7.js} +3 -2
- package/dist/{reset-password-MJ54ICGP.js.map → reset-password-IZQTDTU7.js.map} +1 -1
- package/dist/{sync-dbt-IDDD4X2Z.js → sync-dbt-6WY7HKP7.js} +3 -1
- package/dist/{sync-dbt-IDDD4X2Z.js.map → sync-dbt-6WY7HKP7.js.map} +1 -1
- package/dist/{test-N4KIIKQN.js → test-WYNX4RYZ.js} +11 -10
- package/dist/{test-N4KIIKQN.js.map → test-WYNX4RYZ.js.map} +1 -1
- package/dist/{update-QHLCWS56.js → update-GWPF5AS6.js} +2 -1
- package/dist/{update-QHLCWS56.js.map → update-GWPF5AS6.js.map} +1 -1
- package/package.json +25 -7
- package/dist/chunk-6GDL3DH4.js +0 -354
- package/dist/chunk-6GDL3DH4.js.map +0 -1
- package/dist/dev-HMLMSTA7.js +0 -106
- package/dist/dev-HMLMSTA7.js.map +0 -1
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
// ../../packages/auth-local/dist/passwords.js
|
|
2
|
+
import bcrypt from "bcryptjs";
|
|
3
|
+
var SALT_ROUNDS = 12;
|
|
4
|
+
async function hashPassword(password) {
|
|
5
|
+
return bcrypt.hash(password, SALT_ROUNDS);
|
|
6
|
+
}
|
|
7
|
+
async function verifyPassword(password, hash) {
|
|
8
|
+
return bcrypt.compare(password, hash);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// ../../packages/auth-local/dist/database.js
|
|
12
|
+
import Database from "better-sqlite3";
|
|
13
|
+
import { randomBytes } from "crypto";
|
|
14
|
+
import { v4 as uuidv4 } from "uuid";
|
|
15
|
+
var SCHEMA_SQL = `
|
|
16
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
17
|
+
id TEXT PRIMARY KEY,
|
|
18
|
+
email TEXT UNIQUE NOT NULL,
|
|
19
|
+
name TEXT NOT NULL,
|
|
20
|
+
password_hash TEXT,
|
|
21
|
+
role TEXT NOT NULL DEFAULT 'viewer',
|
|
22
|
+
provider TEXT NOT NULL DEFAULT 'local',
|
|
23
|
+
external_id TEXT,
|
|
24
|
+
created_at TEXT NOT NULL,
|
|
25
|
+
updated_at TEXT NOT NULL,
|
|
26
|
+
UNIQUE(provider, external_id)
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
30
|
+
id TEXT PRIMARY KEY,
|
|
31
|
+
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
32
|
+
expires_at TEXT NOT NULL,
|
|
33
|
+
created_at TEXT NOT NULL
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
CREATE TABLE IF NOT EXISTS password_reset_tokens (
|
|
37
|
+
id TEXT PRIMARY KEY,
|
|
38
|
+
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
39
|
+
token TEXT UNIQUE NOT NULL,
|
|
40
|
+
expires_at TEXT NOT NULL,
|
|
41
|
+
used INTEGER NOT NULL DEFAULT 0
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
CREATE TABLE IF NOT EXISTS share_links (
|
|
45
|
+
id TEXT PRIMARY KEY,
|
|
46
|
+
token TEXT UNIQUE NOT NULL,
|
|
47
|
+
resource_type TEXT NOT NULL,
|
|
48
|
+
resource_name TEXT NOT NULL,
|
|
49
|
+
created_by TEXT NOT NULL,
|
|
50
|
+
created_at TEXT NOT NULL,
|
|
51
|
+
expires_at TEXT,
|
|
52
|
+
is_active INTEGER NOT NULL DEFAULT 1
|
|
53
|
+
);
|
|
54
|
+
`;
|
|
55
|
+
function toSafeUser(user) {
|
|
56
|
+
return {
|
|
57
|
+
id: user.id,
|
|
58
|
+
email: user.email,
|
|
59
|
+
name: user.name,
|
|
60
|
+
role: user.role,
|
|
61
|
+
provider: user.provider,
|
|
62
|
+
attributes: user.attributes,
|
|
63
|
+
created_at: user.created_at,
|
|
64
|
+
updated_at: user.updated_at
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
var AuthDatabase = class {
|
|
68
|
+
db;
|
|
69
|
+
constructor(dbPath) {
|
|
70
|
+
this.db = new Database(dbPath);
|
|
71
|
+
this.db.pragma("journal_mode = WAL");
|
|
72
|
+
this.db.pragma("foreign_keys = ON");
|
|
73
|
+
this.db.exec(SCHEMA_SQL);
|
|
74
|
+
try {
|
|
75
|
+
this.db.prepare("SELECT attributes FROM users LIMIT 0").get();
|
|
76
|
+
} catch {
|
|
77
|
+
this.db.exec("ALTER TABLE users ADD COLUMN attributes TEXT NOT NULL DEFAULT '{}'");
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
close() {
|
|
81
|
+
this.db.close();
|
|
82
|
+
}
|
|
83
|
+
needsSetup() {
|
|
84
|
+
return this.getUserCount() === 0;
|
|
85
|
+
}
|
|
86
|
+
getUserCount() {
|
|
87
|
+
const row = this.db.prepare("SELECT COUNT(*) as count FROM users").get();
|
|
88
|
+
return row.count;
|
|
89
|
+
}
|
|
90
|
+
getAdminCount() {
|
|
91
|
+
const row = this.db.prepare("SELECT COUNT(*) as count FROM users WHERE role = 'admin'").get();
|
|
92
|
+
return row.count;
|
|
93
|
+
}
|
|
94
|
+
createUser(input) {
|
|
95
|
+
const id = uuidv4();
|
|
96
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
97
|
+
this.db.prepare(`
|
|
98
|
+
INSERT INTO users (id, email, name, password_hash, role, provider, external_id, created_at, updated_at)
|
|
99
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
100
|
+
`).run(id, input.email, input.name, input.password_hash, input.role, input.provider ?? "local", input.external_id ?? null, now, now);
|
|
101
|
+
return this.getUserById(id);
|
|
102
|
+
}
|
|
103
|
+
getUserById(id) {
|
|
104
|
+
return this.db.prepare("SELECT * FROM users WHERE id = ?").get(id) ?? null;
|
|
105
|
+
}
|
|
106
|
+
getUserByEmail(email) {
|
|
107
|
+
return this.db.prepare("SELECT * FROM users WHERE email = ?").get(email) ?? null;
|
|
108
|
+
}
|
|
109
|
+
listUsers() {
|
|
110
|
+
const users = this.db.prepare("SELECT * FROM users ORDER BY created_at ASC").all();
|
|
111
|
+
return users.map(toSafeUser);
|
|
112
|
+
}
|
|
113
|
+
updateUser(id, input) {
|
|
114
|
+
if (input.role && input.role !== "admin") {
|
|
115
|
+
const user = this.getUserById(id);
|
|
116
|
+
if (user && user.role === "admin" && this.getAdminCount() === 1) {
|
|
117
|
+
throw new Error("Cannot change role of last admin user");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const sets = [];
|
|
121
|
+
const values = [];
|
|
122
|
+
if (input.name !== void 0) {
|
|
123
|
+
sets.push("name = ?");
|
|
124
|
+
values.push(input.name);
|
|
125
|
+
}
|
|
126
|
+
if (input.role !== void 0) {
|
|
127
|
+
sets.push("role = ?");
|
|
128
|
+
values.push(input.role);
|
|
129
|
+
}
|
|
130
|
+
if (input.password_hash !== void 0) {
|
|
131
|
+
sets.push("password_hash = ?");
|
|
132
|
+
values.push(input.password_hash);
|
|
133
|
+
}
|
|
134
|
+
if (sets.length === 0)
|
|
135
|
+
return;
|
|
136
|
+
sets.push("updated_at = ?");
|
|
137
|
+
values.push((/* @__PURE__ */ new Date()).toISOString());
|
|
138
|
+
values.push(id);
|
|
139
|
+
this.db.prepare(`UPDATE users SET ${sets.join(", ")} WHERE id = ?`).run(...values);
|
|
140
|
+
}
|
|
141
|
+
deleteUser(id) {
|
|
142
|
+
const user = this.getUserById(id);
|
|
143
|
+
if (user && user.role === "admin" && this.getAdminCount() === 1) {
|
|
144
|
+
throw new Error("Cannot delete last admin user");
|
|
145
|
+
}
|
|
146
|
+
this.db.prepare("DELETE FROM users WHERE id = ?").run(id);
|
|
147
|
+
}
|
|
148
|
+
createSession(token, userId, ttlMs) {
|
|
149
|
+
const now = /* @__PURE__ */ new Date();
|
|
150
|
+
const expiresAt = new Date(now.getTime() + ttlMs);
|
|
151
|
+
this.db.prepare(`
|
|
152
|
+
INSERT INTO sessions (id, user_id, expires_at, created_at)
|
|
153
|
+
VALUES (?, ?, ?, ?)
|
|
154
|
+
`).run(token, userId, expiresAt.toISOString(), now.toISOString());
|
|
155
|
+
}
|
|
156
|
+
validateSession(token) {
|
|
157
|
+
const session = this.db.prepare("SELECT * FROM sessions WHERE id = ? AND expires_at > ?").get(token, (/* @__PURE__ */ new Date()).toISOString());
|
|
158
|
+
return session ?? null;
|
|
159
|
+
}
|
|
160
|
+
deleteSession(token) {
|
|
161
|
+
this.db.prepare("DELETE FROM sessions WHERE id = ?").run(token);
|
|
162
|
+
}
|
|
163
|
+
deleteUserSessions(userId) {
|
|
164
|
+
this.db.prepare("DELETE FROM sessions WHERE user_id = ?").run(userId);
|
|
165
|
+
}
|
|
166
|
+
purgeExpiredSessions() {
|
|
167
|
+
const result = this.db.prepare("DELETE FROM sessions WHERE expires_at <= ?").run((/* @__PURE__ */ new Date()).toISOString());
|
|
168
|
+
return result.changes;
|
|
169
|
+
}
|
|
170
|
+
getUserByProviderAndExternalId(provider, externalId) {
|
|
171
|
+
return this.db.prepare("SELECT * FROM users WHERE provider = ? AND external_id = ?").get(provider, externalId) ?? null;
|
|
172
|
+
}
|
|
173
|
+
// --- User Attributes ---
|
|
174
|
+
getUserAttributes(id) {
|
|
175
|
+
const row = this.db.prepare("SELECT attributes FROM users WHERE id = ?").get(id);
|
|
176
|
+
if (!row)
|
|
177
|
+
return {};
|
|
178
|
+
try {
|
|
179
|
+
return JSON.parse(row.attributes);
|
|
180
|
+
} catch {
|
|
181
|
+
return {};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
updateUserAttributes(id, attributes) {
|
|
185
|
+
this.db.prepare("UPDATE users SET attributes = ?, updated_at = ? WHERE id = ?").run(JSON.stringify(attributes), (/* @__PURE__ */ new Date()).toISOString(), id);
|
|
186
|
+
}
|
|
187
|
+
// --- Share Links ---
|
|
188
|
+
createShareLink(input) {
|
|
189
|
+
const id = uuidv4();
|
|
190
|
+
const token = randomBytes(32).toString("hex");
|
|
191
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
192
|
+
this.db.prepare(`
|
|
193
|
+
INSERT INTO share_links (id, token, resource_type, resource_name, created_by, created_at, expires_at, is_active)
|
|
194
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 1)
|
|
195
|
+
`).run(id, token, input.resourceType, input.resourceName, input.createdBy, now, input.expiresAt ?? null);
|
|
196
|
+
return this.db.prepare("SELECT * FROM share_links WHERE id = ?").get(id);
|
|
197
|
+
}
|
|
198
|
+
getShareLinkByToken(token) {
|
|
199
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
200
|
+
const link = this.db.prepare("SELECT * FROM share_links WHERE token = ? AND is_active = 1 AND (expires_at IS NULL OR expires_at > ?)").get(token, now);
|
|
201
|
+
return link ?? null;
|
|
202
|
+
}
|
|
203
|
+
listShareLinks(createdBy) {
|
|
204
|
+
if (createdBy) {
|
|
205
|
+
return this.db.prepare("SELECT * FROM share_links WHERE created_by = ? AND is_active = 1 ORDER BY created_at DESC").all(createdBy);
|
|
206
|
+
}
|
|
207
|
+
return this.db.prepare("SELECT * FROM share_links WHERE is_active = 1 ORDER BY created_at DESC").all();
|
|
208
|
+
}
|
|
209
|
+
revokeShareLink(id) {
|
|
210
|
+
this.db.prepare("UPDATE share_links SET is_active = 0 WHERE id = ?").run(id);
|
|
211
|
+
}
|
|
212
|
+
deleteShareLink(id) {
|
|
213
|
+
this.db.prepare("DELETE FROM share_links WHERE id = ?").run(id);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// ../../packages/auth-local/dist/sessions.js
|
|
218
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
219
|
+
function generateSessionToken() {
|
|
220
|
+
return randomBytes2(32).toString("hex");
|
|
221
|
+
}
|
|
222
|
+
var DEFAULT_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
223
|
+
function parseTtl(ttl) {
|
|
224
|
+
const match = ttl.match(/^(\d+)(d|h|m)$/);
|
|
225
|
+
if (!match || !match[1] || !match[2])
|
|
226
|
+
return DEFAULT_TTL_MS;
|
|
227
|
+
const value = parseInt(match[1], 10);
|
|
228
|
+
switch (match[2]) {
|
|
229
|
+
case "d":
|
|
230
|
+
return value * 24 * 60 * 60 * 1e3;
|
|
231
|
+
case "h":
|
|
232
|
+
return value * 60 * 60 * 1e3;
|
|
233
|
+
case "m":
|
|
234
|
+
return value * 60 * 1e3;
|
|
235
|
+
default:
|
|
236
|
+
return DEFAULT_TTL_MS;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export {
|
|
241
|
+
hashPassword,
|
|
242
|
+
verifyPassword,
|
|
243
|
+
AuthDatabase,
|
|
244
|
+
generateSessionToken,
|
|
245
|
+
parseTtl
|
|
246
|
+
};
|
|
247
|
+
//# sourceMappingURL=chunk-4P5UHWYK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../packages/auth-local/src/passwords.ts","../../../packages/auth-local/src/database.ts","../../../packages/auth-local/src/sessions.ts"],"sourcesContent":["import bcrypt from 'bcryptjs';\n\nconst SALT_ROUNDS = 12;\n\nexport async function hashPassword(password: string): Promise<string> {\n return bcrypt.hash(password, SALT_ROUNDS);\n}\n\nexport async function verifyPassword(password: string, hash: string): Promise<boolean> {\n return bcrypt.compare(password, hash);\n}\n","import Database from 'better-sqlite3';\nimport { randomBytes } from 'crypto';\nimport { v4 as uuidv4 } from 'uuid';\nimport type { LocalUser, SafeUser, Role, Provider, Session, ShareLink, CreateShareLinkInput } from './types.js';\n\nconst SCHEMA_SQL = `\nCREATE TABLE IF NOT EXISTS users (\n id TEXT PRIMARY KEY,\n email TEXT UNIQUE NOT NULL,\n name TEXT NOT NULL,\n password_hash TEXT,\n role TEXT NOT NULL DEFAULT 'viewer',\n provider TEXT NOT NULL DEFAULT 'local',\n external_id TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE(provider, external_id)\n);\n\nCREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,\n expires_at TEXT NOT NULL,\n created_at TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS password_reset_tokens (\n id TEXT PRIMARY KEY,\n user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,\n token TEXT UNIQUE NOT NULL,\n expires_at TEXT NOT NULL,\n used INTEGER NOT NULL DEFAULT 0\n);\n\nCREATE TABLE IF NOT EXISTS share_links (\n id TEXT PRIMARY KEY,\n token TEXT UNIQUE NOT NULL,\n resource_type TEXT NOT NULL,\n resource_name TEXT NOT NULL,\n created_by TEXT NOT NULL,\n created_at TEXT NOT NULL,\n expires_at TEXT,\n is_active INTEGER NOT NULL DEFAULT 1\n);\n`;\n\nexport interface CreateUserInput {\n email: string;\n name: string;\n password_hash: string | null;\n role: Role;\n provider?: Provider;\n external_id?: string | null;\n}\n\nexport interface UpdateUserInput {\n name?: string;\n role?: Role;\n password_hash?: string;\n}\n\nfunction toSafeUser(user: LocalUser): SafeUser {\n return {\n id: user.id,\n email: user.email,\n name: user.name,\n role: user.role,\n provider: user.provider,\n attributes: user.attributes,\n created_at: user.created_at,\n updated_at: user.updated_at,\n };\n}\n\nexport class AuthDatabase {\n private db: Database.Database;\n\n constructor(dbPath: string) {\n this.db = new Database(dbPath);\n this.db.pragma('journal_mode = WAL');\n this.db.pragma('foreign_keys = ON');\n this.db.exec(SCHEMA_SQL);\n\n // Migrate: add attributes column if missing\n try {\n this.db.prepare(\"SELECT attributes FROM users LIMIT 0\").get();\n } catch {\n this.db.exec(\"ALTER TABLE users ADD COLUMN attributes TEXT NOT NULL DEFAULT '{}'\");\n }\n }\n\n close(): void {\n this.db.close();\n }\n\n needsSetup(): boolean {\n return this.getUserCount() === 0;\n }\n\n getUserCount(): number {\n const row = this.db.prepare('SELECT COUNT(*) as count FROM users').get() as { count: number };\n return row.count;\n }\n\n private getAdminCount(): number {\n const row = this.db.prepare(\"SELECT COUNT(*) as count FROM users WHERE role = 'admin'\").get() as { count: number };\n return row.count;\n }\n\n createUser(input: CreateUserInput): SafeUser {\n const id = uuidv4();\n const now = new Date().toISOString();\n\n this.db.prepare(`\n INSERT INTO users (id, email, name, password_hash, role, provider, external_id, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `).run(\n id,\n input.email,\n input.name,\n input.password_hash,\n input.role,\n input.provider ?? 'local',\n input.external_id ?? null,\n now,\n now,\n );\n\n return this.getUserById(id)! as SafeUser;\n }\n\n getUserById(id: string): LocalUser | null {\n return (this.db.prepare('SELECT * FROM users WHERE id = ?').get(id) as LocalUser) ?? null;\n }\n\n getUserByEmail(email: string): LocalUser | null {\n return (this.db.prepare('SELECT * FROM users WHERE email = ?').get(email) as LocalUser) ?? null;\n }\n\n listUsers(): SafeUser[] {\n const users = this.db.prepare('SELECT * FROM users ORDER BY created_at ASC').all() as LocalUser[];\n return users.map(toSafeUser);\n }\n\n updateUser(id: string, input: UpdateUserInput): void {\n // Guard: prevent changing last admin's role\n if (input.role && input.role !== 'admin') {\n const user = this.getUserById(id);\n if (user && user.role === 'admin' && this.getAdminCount() === 1) {\n throw new Error('Cannot change role of last admin user');\n }\n }\n\n const sets: string[] = [];\n const values: unknown[] = [];\n\n if (input.name !== undefined) { sets.push('name = ?'); values.push(input.name); }\n if (input.role !== undefined) { sets.push('role = ?'); values.push(input.role); }\n if (input.password_hash !== undefined) { sets.push('password_hash = ?'); values.push(input.password_hash); }\n\n if (sets.length === 0) return;\n\n sets.push('updated_at = ?');\n values.push(new Date().toISOString());\n values.push(id);\n\n this.db.prepare(`UPDATE users SET ${sets.join(', ')} WHERE id = ?`).run(...values);\n }\n\n deleteUser(id: string): void {\n const user = this.getUserById(id);\n if (user && user.role === 'admin' && this.getAdminCount() === 1) {\n throw new Error('Cannot delete last admin user');\n }\n this.db.prepare('DELETE FROM users WHERE id = ?').run(id);\n }\n\n createSession(token: string, userId: string, ttlMs: number): void {\n const now = new Date();\n const expiresAt = new Date(now.getTime() + ttlMs);\n\n this.db.prepare(`\n INSERT INTO sessions (id, user_id, expires_at, created_at)\n VALUES (?, ?, ?, ?)\n `).run(token, userId, expiresAt.toISOString(), now.toISOString());\n }\n\n validateSession(token: string): Session | null {\n const session = this.db.prepare(\n 'SELECT * FROM sessions WHERE id = ? AND expires_at > ?'\n ).get(token, new Date().toISOString()) as Session | undefined;\n\n return session ?? null;\n }\n\n deleteSession(token: string): void {\n this.db.prepare('DELETE FROM sessions WHERE id = ?').run(token);\n }\n\n deleteUserSessions(userId: string): void {\n this.db.prepare('DELETE FROM sessions WHERE user_id = ?').run(userId);\n }\n\n purgeExpiredSessions(): number {\n const result = this.db.prepare(\n 'DELETE FROM sessions WHERE expires_at <= ?'\n ).run(new Date().toISOString());\n return result.changes;\n }\n\n getUserByProviderAndExternalId(provider: string, externalId: string): LocalUser | null {\n return (this.db.prepare(\n 'SELECT * FROM users WHERE provider = ? AND external_id = ?'\n ).get(provider, externalId) as LocalUser) ?? null;\n }\n\n // --- User Attributes ---\n\n getUserAttributes(id: string): Record<string, string> {\n const row = this.db.prepare('SELECT attributes FROM users WHERE id = ?').get(id) as { attributes: string } | undefined;\n if (!row) return {};\n try {\n return JSON.parse(row.attributes);\n } catch {\n return {};\n }\n }\n\n updateUserAttributes(id: string, attributes: Record<string, string>): void {\n this.db.prepare('UPDATE users SET attributes = ?, updated_at = ? WHERE id = ?')\n .run(JSON.stringify(attributes), new Date().toISOString(), id);\n }\n\n // --- Share Links ---\n\n createShareLink(input: CreateShareLinkInput): ShareLink {\n const id = uuidv4();\n const token = randomBytes(32).toString('hex');\n const now = new Date().toISOString();\n\n this.db.prepare(`\n INSERT INTO share_links (id, token, resource_type, resource_name, created_by, created_at, expires_at, is_active)\n VALUES (?, ?, ?, ?, ?, ?, ?, 1)\n `).run(id, token, input.resourceType, input.resourceName, input.createdBy, now, input.expiresAt ?? null);\n\n return this.db.prepare('SELECT * FROM share_links WHERE id = ?').get(id) as ShareLink;\n }\n\n getShareLinkByToken(token: string): ShareLink | null {\n const now = new Date().toISOString();\n const link = this.db.prepare(\n 'SELECT * FROM share_links WHERE token = ? AND is_active = 1 AND (expires_at IS NULL OR expires_at > ?)'\n ).get(token, now) as ShareLink | undefined;\n return link ?? null;\n }\n\n listShareLinks(createdBy?: string): ShareLink[] {\n if (createdBy) {\n return this.db.prepare(\n 'SELECT * FROM share_links WHERE created_by = ? AND is_active = 1 ORDER BY created_at DESC'\n ).all(createdBy) as ShareLink[];\n }\n return this.db.prepare(\n 'SELECT * FROM share_links WHERE is_active = 1 ORDER BY created_at DESC'\n ).all() as ShareLink[];\n }\n\n revokeShareLink(id: string): void {\n this.db.prepare('UPDATE share_links SET is_active = 0 WHERE id = ?').run(id);\n }\n\n deleteShareLink(id: string): void {\n this.db.prepare('DELETE FROM share_links WHERE id = ?').run(id);\n }\n}\n","import { randomBytes } from 'crypto';\n\nexport function generateSessionToken(): string {\n return randomBytes(32).toString('hex');\n}\n\nconst DEFAULT_TTL_MS = 30 * 24 * 60 * 60 * 1000; // 30 days\n\nexport function parseTtl(ttl: string): number {\n const match = ttl.match(/^(\\d+)(d|h|m)$/);\n if (!match || !match[1] || !match[2]) return DEFAULT_TTL_MS;\n\n const value = parseInt(match[1], 10);\n switch (match[2]) {\n case 'd': return value * 24 * 60 * 60 * 1000;\n case 'h': return value * 60 * 60 * 1000;\n case 'm': return value * 60 * 1000;\n default: return DEFAULT_TTL_MS;\n }\n}\n"],"mappings":";AAAA,OAAO,YAAY;AAEnB,IAAM,cAAc;AAEpB,eAAsB,aAAa,UAAgB;AACjD,SAAO,OAAO,KAAK,UAAU,WAAW;AAC1C;AAEA,eAAsB,eAAe,UAAkB,MAAY;AACjE,SAAO,OAAO,QAAQ,UAAU,IAAI;AACtC;;;ACVA,OAAO,cAAc;AACrB,SAAS,mBAAmB;AAC5B,SAAS,MAAM,cAAc;AAG7B,IAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwDnB,SAAS,WAAW,MAAe;AACjC,SAAO;IACL,IAAI,KAAK;IACT,OAAO,KAAK;IACZ,MAAM,KAAK;IACX,MAAM,KAAK;IACX,UAAU,KAAK;IACf,YAAY,KAAK;IACjB,YAAY,KAAK;IACjB,YAAY,KAAK;;AAErB;AAEM,IAAO,eAAP,MAAmB;EACf;EAER,YAAY,QAAc;AACxB,SAAK,KAAK,IAAI,SAAS,MAAM;AAC7B,SAAK,GAAG,OAAO,oBAAoB;AACnC,SAAK,GAAG,OAAO,mBAAmB;AAClC,SAAK,GAAG,KAAK,UAAU;AAGvB,QAAI;AACF,WAAK,GAAG,QAAQ,sCAAsC,EAAE,IAAG;IAC7D,QAAQ;AACN,WAAK,GAAG,KAAK,oEAAoE;IACnF;EACF;EAEA,QAAK;AACH,SAAK,GAAG,MAAK;EACf;EAEA,aAAU;AACR,WAAO,KAAK,aAAY,MAAO;EACjC;EAEA,eAAY;AACV,UAAM,MAAM,KAAK,GAAG,QAAQ,qCAAqC,EAAE,IAAG;AACtE,WAAO,IAAI;EACb;EAEQ,gBAAa;AACnB,UAAM,MAAM,KAAK,GAAG,QAAQ,0DAA0D,EAAE,IAAG;AAC3F,WAAO,IAAI;EACb;EAEA,WAAW,OAAsB;AAC/B,UAAM,KAAK,OAAM;AACjB,UAAM,OAAM,oBAAI,KAAI,GAAG,YAAW;AAElC,SAAK,GAAG,QAAQ;;;KAGf,EAAE,IACD,IACA,MAAM,OACN,MAAM,MACN,MAAM,eACN,MAAM,MACN,MAAM,YAAY,SAClB,MAAM,eAAe,MACrB,KACA,GAAG;AAGL,WAAO,KAAK,YAAY,EAAE;EAC5B;EAEA,YAAY,IAAU;AACpB,WAAQ,KAAK,GAAG,QAAQ,kCAAkC,EAAE,IAAI,EAAE,KAAmB;EACvF;EAEA,eAAe,OAAa;AAC1B,WAAQ,KAAK,GAAG,QAAQ,qCAAqC,EAAE,IAAI,KAAK,KAAmB;EAC7F;EAEA,YAAS;AACP,UAAM,QAAQ,KAAK,GAAG,QAAQ,6CAA6C,EAAE,IAAG;AAChF,WAAO,MAAM,IAAI,UAAU;EAC7B;EAEA,WAAW,IAAY,OAAsB;AAE3C,QAAI,MAAM,QAAQ,MAAM,SAAS,SAAS;AACxC,YAAM,OAAO,KAAK,YAAY,EAAE;AAChC,UAAI,QAAQ,KAAK,SAAS,WAAW,KAAK,cAAa,MAAO,GAAG;AAC/D,cAAM,IAAI,MAAM,uCAAuC;MACzD;IACF;AAEA,UAAM,OAAiB,CAAA;AACvB,UAAM,SAAoB,CAAA;AAE1B,QAAI,MAAM,SAAS,QAAW;AAAE,WAAK,KAAK,UAAU;AAAG,aAAO,KAAK,MAAM,IAAI;IAAG;AAChF,QAAI,MAAM,SAAS,QAAW;AAAE,WAAK,KAAK,UAAU;AAAG,aAAO,KAAK,MAAM,IAAI;IAAG;AAChF,QAAI,MAAM,kBAAkB,QAAW;AAAE,WAAK,KAAK,mBAAmB;AAAG,aAAO,KAAK,MAAM,aAAa;IAAG;AAE3G,QAAI,KAAK,WAAW;AAAG;AAEvB,SAAK,KAAK,gBAAgB;AAC1B,WAAO,MAAK,oBAAI,KAAI,GAAG,YAAW,CAAE;AACpC,WAAO,KAAK,EAAE;AAEd,SAAK,GAAG,QAAQ,oBAAoB,KAAK,KAAK,IAAI,CAAC,eAAe,EAAE,IAAI,GAAG,MAAM;EACnF;EAEA,WAAW,IAAU;AACnB,UAAM,OAAO,KAAK,YAAY,EAAE;AAChC,QAAI,QAAQ,KAAK,SAAS,WAAW,KAAK,cAAa,MAAO,GAAG;AAC/D,YAAM,IAAI,MAAM,+BAA+B;IACjD;AACA,SAAK,GAAG,QAAQ,gCAAgC,EAAE,IAAI,EAAE;EAC1D;EAEA,cAAc,OAAe,QAAgB,OAAa;AACxD,UAAM,MAAM,oBAAI,KAAI;AACpB,UAAM,YAAY,IAAI,KAAK,IAAI,QAAO,IAAK,KAAK;AAEhD,SAAK,GAAG,QAAQ;;;KAGf,EAAE,IAAI,OAAO,QAAQ,UAAU,YAAW,GAAI,IAAI,YAAW,CAAE;EAClE;EAEA,gBAAgB,OAAa;AAC3B,UAAM,UAAU,KAAK,GAAG,QACtB,wDAAwD,EACxD,IAAI,QAAO,oBAAI,KAAI,GAAG,YAAW,CAAE;AAErC,WAAO,WAAW;EACpB;EAEA,cAAc,OAAa;AACzB,SAAK,GAAG,QAAQ,mCAAmC,EAAE,IAAI,KAAK;EAChE;EAEA,mBAAmB,QAAc;AAC/B,SAAK,GAAG,QAAQ,wCAAwC,EAAE,IAAI,MAAM;EACtE;EAEA,uBAAoB;AAClB,UAAM,SAAS,KAAK,GAAG,QACrB,4CAA4C,EAC5C,KAAI,oBAAI,KAAI,GAAG,YAAW,CAAE;AAC9B,WAAO,OAAO;EAChB;EAEA,+BAA+B,UAAkB,YAAkB;AACjE,WAAQ,KAAK,GAAG,QACd,4DAA4D,EAC5D,IAAI,UAAU,UAAU,KAAmB;EAC/C;;EAIA,kBAAkB,IAAU;AAC1B,UAAM,MAAM,KAAK,GAAG,QAAQ,2CAA2C,EAAE,IAAI,EAAE;AAC/E,QAAI,CAAC;AAAK,aAAO,CAAA;AACjB,QAAI;AACF,aAAO,KAAK,MAAM,IAAI,UAAU;IAClC,QAAQ;AACN,aAAO,CAAA;IACT;EACF;EAEA,qBAAqB,IAAY,YAAkC;AACjE,SAAK,GAAG,QAAQ,8DAA8D,EAC3E,IAAI,KAAK,UAAU,UAAU,IAAG,oBAAI,KAAI,GAAG,YAAW,GAAI,EAAE;EACjE;;EAIA,gBAAgB,OAA2B;AACzC,UAAM,KAAK,OAAM;AACjB,UAAM,QAAQ,YAAY,EAAE,EAAE,SAAS,KAAK;AAC5C,UAAM,OAAM,oBAAI,KAAI,GAAG,YAAW;AAElC,SAAK,GAAG,QAAQ;;;KAGf,EAAE,IAAI,IAAI,OAAO,MAAM,cAAc,MAAM,cAAc,MAAM,WAAW,KAAK,MAAM,aAAa,IAAI;AAEvG,WAAO,KAAK,GAAG,QAAQ,wCAAwC,EAAE,IAAI,EAAE;EACzE;EAEA,oBAAoB,OAAa;AAC/B,UAAM,OAAM,oBAAI,KAAI,GAAG,YAAW;AAClC,UAAM,OAAO,KAAK,GAAG,QACnB,wGAAwG,EACxG,IAAI,OAAO,GAAG;AAChB,WAAO,QAAQ;EACjB;EAEA,eAAe,WAAkB;AAC/B,QAAI,WAAW;AACb,aAAO,KAAK,GAAG,QACb,2FAA2F,EAC3F,IAAI,SAAS;IACjB;AACA,WAAO,KAAK,GAAG,QACb,wEAAwE,EACxE,IAAG;EACP;EAEA,gBAAgB,IAAU;AACxB,SAAK,GAAG,QAAQ,mDAAmD,EAAE,IAAI,EAAE;EAC7E;EAEA,gBAAgB,IAAU;AACxB,SAAK,GAAG,QAAQ,sCAAsC,EAAE,IAAI,EAAE;EAChE;;;;ACjRF,SAAS,eAAAA,oBAAmB;AAEtB,SAAU,uBAAoB;AAClC,SAAOA,aAAY,EAAE,EAAE,SAAS,KAAK;AACvC;AAEA,IAAM,iBAAiB,KAAK,KAAK,KAAK,KAAK;AAErC,SAAU,SAAS,KAAW;AAClC,QAAM,QAAQ,IAAI,MAAM,gBAAgB;AACxC,MAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;AAAG,WAAO;AAE7C,QAAM,QAAQ,SAAS,MAAM,CAAC,GAAG,EAAE;AACnC,UAAQ,MAAM,CAAC,GAAG;IAChB,KAAK;AAAK,aAAO,QAAQ,KAAK,KAAK,KAAK;IACxC,KAAK;AAAK,aAAO,QAAQ,KAAK,KAAK;IACnC,KAAK;AAAK,aAAO,QAAQ,KAAK;IAC9B;AAAS,aAAO;EAClB;AACF;","names":["randomBytes"]}
|