@rankmyseo/storage 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,605 @@
1
+ // src/index.ts
2
+ import "server-only";
3
+
4
+ // src/factory.ts
5
+ import "server-only";
6
+
7
+ // src/sqlite-store.ts
8
+ import "server-only";
9
+ import { randomUUID } from "crypto";
10
+ import Database from "better-sqlite3";
11
+ import { drizzle } from "drizzle-orm/better-sqlite3";
12
+ import { and, eq, gte, lte } from "drizzle-orm";
13
+
14
+ // src/schema/sqlite.ts
15
+ import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
16
+ var projects = sqliteTable("rms_projects", {
17
+ id: text("id").primaryKey(),
18
+ tenantId: text("tenant_id").notNull(),
19
+ name: text("name").notNull(),
20
+ domain: text("domain").notNull(),
21
+ createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
22
+ updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull()
23
+ });
24
+ var keywords = sqliteTable("rms_keywords", {
25
+ id: text("id").primaryKey(),
26
+ tenantId: text("tenant_id").notNull(),
27
+ projectId: text("project_id").notNull(),
28
+ text: text("text").notNull(),
29
+ country: text("country").notNull(),
30
+ device: text("device").notNull(),
31
+ tags: text("tags").notNull(),
32
+ createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull()
33
+ });
34
+ var rankSnapshots = sqliteTable("rms_rank_snapshots", {
35
+ id: text("id").primaryKey(),
36
+ tenantId: text("tenant_id").notNull(),
37
+ projectId: text("project_id").notNull(),
38
+ keywordId: text("keyword_id").notNull(),
39
+ position: integer("position"),
40
+ url: text("url"),
41
+ source: text("source").notNull(),
42
+ device: text("device").notNull(),
43
+ country: text("country").notNull(),
44
+ capturedAt: integer("captured_at", { mode: "timestamp_ms" }).notNull(),
45
+ serpFeatures: text("serp_features")
46
+ });
47
+ var audits = sqliteTable("rms_audits", {
48
+ id: text("id").primaryKey(),
49
+ tenantId: text("tenant_id").notNull(),
50
+ projectId: text("project_id").notNull(),
51
+ url: text("url").notNull(),
52
+ score: integer("score").notNull(),
53
+ checks: text("checks").notNull().default("[]"),
54
+ createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull()
55
+ });
56
+ var reports = sqliteTable("rms_reports", {
57
+ id: text("id").primaryKey(),
58
+ tenantId: text("tenant_id").notNull(),
59
+ projectId: text("project_id").notNull(),
60
+ title: text("title").notNull(),
61
+ from: integer("from", { mode: "timestamp_ms" }).notNull(),
62
+ to: integer("to", { mode: "timestamp_ms" }).notNull(),
63
+ summary: text("summary"),
64
+ createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull()
65
+ });
66
+ var dashboardConfigs = sqliteTable("rms_dashboard_configs", {
67
+ id: text("id").primaryKey(),
68
+ tenantId: text("tenant_id").notNull(),
69
+ projectId: text("project_id").notNull(),
70
+ widgets: text("widgets").notNull(),
71
+ updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull()
72
+ });
73
+ var blogPosts = sqliteTable("rms_blog_posts", {
74
+ id: text("id").primaryKey(),
75
+ tenantId: text("tenant_id").notNull(),
76
+ projectId: text("project_id").notNull(),
77
+ title: text("title").notNull(),
78
+ slug: text("slug").notNull(),
79
+ content: text("content").notNull().default(""),
80
+ targetKeyword: text("target_keyword").notNull().default(""),
81
+ intent: text("intent").notNull().default("informational"),
82
+ metaTitle: text("meta_title").notNull().default(""),
83
+ metaDescription: text("meta_description").notNull().default(""),
84
+ status: text("status").notNull().default("draft"),
85
+ createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
86
+ updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull()
87
+ });
88
+ var sqliteSchema = {
89
+ projects,
90
+ keywords,
91
+ rankSnapshots,
92
+ audits,
93
+ reports,
94
+ dashboardConfigs,
95
+ blogPosts
96
+ };
97
+
98
+ // src/sqlite-store.ts
99
+ function mapBlogRow(row) {
100
+ return {
101
+ id: row.id,
102
+ tenantId: row.tenantId,
103
+ projectId: row.projectId,
104
+ title: row.title,
105
+ slug: row.slug,
106
+ content: row.content,
107
+ targetKeyword: row.targetKeyword,
108
+ intent: row.intent,
109
+ metaTitle: row.metaTitle,
110
+ metaDescription: row.metaDescription,
111
+ status: row.status,
112
+ createdAt: row.createdAt,
113
+ updatedAt: row.updatedAt
114
+ };
115
+ }
116
+ function parseChecks(raw) {
117
+ if (!raw) return [];
118
+ try {
119
+ const parsed = JSON.parse(raw);
120
+ return Array.isArray(parsed) ? parsed : [];
121
+ } catch {
122
+ return [];
123
+ }
124
+ }
125
+ function parseSummary(raw) {
126
+ if (!raw) return void 0;
127
+ try {
128
+ return JSON.parse(raw);
129
+ } catch {
130
+ return void 0;
131
+ }
132
+ }
133
+ function mapAuditRow(row) {
134
+ return {
135
+ id: row.id,
136
+ tenantId: row.tenantId,
137
+ projectId: row.projectId,
138
+ url: row.url,
139
+ score: row.score,
140
+ checks: parseChecks(row.checks),
141
+ createdAt: row.createdAt
142
+ };
143
+ }
144
+ function mapReportRow(row) {
145
+ return {
146
+ id: row.id,
147
+ tenantId: row.tenantId,
148
+ projectId: row.projectId,
149
+ title: row.title,
150
+ from: row.from,
151
+ to: row.to,
152
+ summary: parseSummary(row.summary),
153
+ createdAt: row.createdAt
154
+ };
155
+ }
156
+ function parseTags(raw) {
157
+ try {
158
+ const parsed = JSON.parse(raw);
159
+ return Array.isArray(parsed) ? parsed.filter((t) => typeof t === "string") : [];
160
+ } catch {
161
+ return [];
162
+ }
163
+ }
164
+ function migrate(sqlite) {
165
+ sqlite.exec(`
166
+ CREATE TABLE IF NOT EXISTS rms_projects (
167
+ id TEXT PRIMARY KEY,
168
+ tenant_id TEXT NOT NULL,
169
+ name TEXT NOT NULL,
170
+ domain TEXT NOT NULL,
171
+ created_at INTEGER NOT NULL,
172
+ updated_at INTEGER NOT NULL
173
+ );
174
+ CREATE TABLE IF NOT EXISTS rms_keywords (
175
+ id TEXT PRIMARY KEY,
176
+ tenant_id TEXT NOT NULL,
177
+ project_id TEXT NOT NULL,
178
+ text TEXT NOT NULL,
179
+ country TEXT NOT NULL,
180
+ device TEXT NOT NULL,
181
+ tags TEXT NOT NULL,
182
+ created_at INTEGER NOT NULL
183
+ );
184
+ CREATE TABLE IF NOT EXISTS rms_rank_snapshots (
185
+ id TEXT PRIMARY KEY,
186
+ tenant_id TEXT NOT NULL,
187
+ project_id TEXT NOT NULL,
188
+ keyword_id TEXT NOT NULL,
189
+ position INTEGER,
190
+ url TEXT,
191
+ source TEXT NOT NULL,
192
+ device TEXT NOT NULL,
193
+ country TEXT NOT NULL,
194
+ captured_at INTEGER NOT NULL,
195
+ serp_features TEXT
196
+ );
197
+ CREATE TABLE IF NOT EXISTS rms_audits (
198
+ id TEXT PRIMARY KEY,
199
+ tenant_id TEXT NOT NULL,
200
+ project_id TEXT NOT NULL,
201
+ url TEXT NOT NULL,
202
+ score INTEGER NOT NULL,
203
+ checks TEXT NOT NULL DEFAULT '[]',
204
+ created_at INTEGER NOT NULL
205
+ );
206
+ CREATE TABLE IF NOT EXISTS rms_reports (
207
+ id TEXT PRIMARY KEY,
208
+ tenant_id TEXT NOT NULL,
209
+ project_id TEXT NOT NULL,
210
+ title TEXT NOT NULL,
211
+ "from" INTEGER NOT NULL,
212
+ "to" INTEGER NOT NULL,
213
+ summary TEXT,
214
+ created_at INTEGER NOT NULL
215
+ );
216
+ CREATE TABLE IF NOT EXISTS rms_dashboard_configs (
217
+ id TEXT PRIMARY KEY,
218
+ tenant_id TEXT NOT NULL,
219
+ project_id TEXT NOT NULL,
220
+ widgets TEXT NOT NULL,
221
+ updated_at INTEGER NOT NULL
222
+ );
223
+ CREATE TABLE IF NOT EXISTS rms_blog_posts (
224
+ id TEXT PRIMARY KEY,
225
+ tenant_id TEXT NOT NULL,
226
+ project_id TEXT NOT NULL,
227
+ title TEXT NOT NULL,
228
+ slug TEXT NOT NULL,
229
+ content TEXT NOT NULL DEFAULT '',
230
+ target_keyword TEXT NOT NULL DEFAULT '',
231
+ intent TEXT NOT NULL DEFAULT 'informational',
232
+ meta_title TEXT NOT NULL DEFAULT '',
233
+ meta_description TEXT NOT NULL DEFAULT '',
234
+ status TEXT NOT NULL DEFAULT 'draft',
235
+ created_at INTEGER NOT NULL,
236
+ updated_at INTEGER NOT NULL
237
+ );
238
+ `);
239
+ try {
240
+ sqlite.exec(`ALTER TABLE rms_audits ADD COLUMN checks TEXT NOT NULL DEFAULT '[]'`);
241
+ } catch {
242
+ }
243
+ try {
244
+ sqlite.exec(`ALTER TABLE rms_reports ADD COLUMN summary TEXT`);
245
+ } catch {
246
+ }
247
+ }
248
+ function createSqliteStore(databasePath) {
249
+ const sqlite = new Database(databasePath);
250
+ migrate(sqlite);
251
+ const db = drizzle(sqlite, { schema: sqliteSchema });
252
+ const keywordScopeWhere = (scope) => and(
253
+ eq(keywords.tenantId, scope.tenantId),
254
+ eq(keywords.projectId, scope.projectId)
255
+ );
256
+ return {
257
+ projects: {
258
+ async create(input) {
259
+ const now = /* @__PURE__ */ new Date();
260
+ const row = {
261
+ ...input,
262
+ createdAt: now,
263
+ updatedAt: now
264
+ };
265
+ db.insert(projects).values({
266
+ id: row.id,
267
+ tenantId: row.tenantId,
268
+ name: row.name,
269
+ domain: row.domain,
270
+ createdAt: row.createdAt,
271
+ updatedAt: row.updatedAt
272
+ }).run();
273
+ return row;
274
+ },
275
+ async getById(scope, id) {
276
+ const rows = db.select().from(projects).where(and(eq(projects.tenantId, scope.tenantId), eq(projects.id, id))).all();
277
+ const row = rows[0];
278
+ if (!row) return void 0;
279
+ return {
280
+ id: row.id,
281
+ tenantId: row.tenantId,
282
+ name: row.name,
283
+ domain: row.domain,
284
+ createdAt: row.createdAt,
285
+ updatedAt: row.updatedAt
286
+ };
287
+ },
288
+ async list(scope) {
289
+ return db.select().from(projects).where(eq(projects.tenantId, scope.tenantId)).all().map((row) => ({
290
+ id: row.id,
291
+ tenantId: row.tenantId,
292
+ name: row.name,
293
+ domain: row.domain,
294
+ createdAt: row.createdAt,
295
+ updatedAt: row.updatedAt
296
+ }));
297
+ }
298
+ },
299
+ keywords: {
300
+ async create(input) {
301
+ const row = {
302
+ id: randomUUID(),
303
+ ...input,
304
+ createdAt: /* @__PURE__ */ new Date()
305
+ };
306
+ db.insert(keywords).values({
307
+ id: row.id,
308
+ tenantId: row.tenantId,
309
+ projectId: row.projectId,
310
+ text: row.text,
311
+ country: row.country,
312
+ device: row.device,
313
+ tags: JSON.stringify(row.tags),
314
+ createdAt: row.createdAt
315
+ }).run();
316
+ return row;
317
+ },
318
+ async getById(scope, id) {
319
+ const rows = db.select().from(keywords).where(and(keywordScopeWhere(scope), eq(keywords.id, id))).all();
320
+ const row = rows[0];
321
+ if (!row) return void 0;
322
+ return {
323
+ id: row.id,
324
+ tenantId: row.tenantId,
325
+ projectId: row.projectId,
326
+ text: row.text,
327
+ country: row.country,
328
+ device: row.device,
329
+ tags: parseTags(row.tags),
330
+ createdAt: row.createdAt
331
+ };
332
+ },
333
+ async list(scope) {
334
+ return db.select().from(keywords).where(keywordScopeWhere(scope)).all().map((row) => ({
335
+ id: row.id,
336
+ tenantId: row.tenantId,
337
+ projectId: row.projectId,
338
+ text: row.text,
339
+ country: row.country,
340
+ device: row.device,
341
+ tags: parseTags(row.tags),
342
+ createdAt: row.createdAt
343
+ }));
344
+ },
345
+ async delete(scope, id) {
346
+ const result = db.delete(keywords).where(and(keywordScopeWhere(scope), eq(keywords.id, id))).run();
347
+ return result.changes > 0;
348
+ }
349
+ },
350
+ snapshots: {
351
+ async append(input) {
352
+ const row = {
353
+ id: randomUUID(),
354
+ ...input
355
+ };
356
+ db.insert(rankSnapshots).values({
357
+ id: row.id,
358
+ tenantId: row.tenantId,
359
+ projectId: row.projectId,
360
+ keywordId: row.keywordId,
361
+ position: row.position,
362
+ url: row.url,
363
+ source: row.source,
364
+ device: row.device,
365
+ country: row.country,
366
+ capturedAt: row.capturedAt,
367
+ serpFeatures: row.serpFeatures ? JSON.stringify(row.serpFeatures) : null
368
+ }).run();
369
+ return row;
370
+ },
371
+ async listByRange(query) {
372
+ const conditions = [
373
+ eq(rankSnapshots.tenantId, query.tenantId),
374
+ eq(rankSnapshots.projectId, query.projectId),
375
+ gte(rankSnapshots.capturedAt, query.from),
376
+ lte(rankSnapshots.capturedAt, query.to)
377
+ ];
378
+ if (query.keywordId) {
379
+ conditions.push(eq(rankSnapshots.keywordId, query.keywordId));
380
+ }
381
+ return db.select().from(rankSnapshots).where(and(...conditions)).all().map((row) => ({
382
+ id: row.id,
383
+ tenantId: row.tenantId,
384
+ projectId: row.projectId,
385
+ keywordId: row.keywordId,
386
+ position: row.position,
387
+ url: row.url,
388
+ source: row.source,
389
+ device: row.device,
390
+ country: row.country,
391
+ capturedAt: row.capturedAt,
392
+ serpFeatures: row.serpFeatures ? JSON.parse(row.serpFeatures) : void 0
393
+ }));
394
+ }
395
+ },
396
+ audits: {
397
+ async create(input) {
398
+ const row = { ...input, checks: input.checks ?? [], createdAt: /* @__PURE__ */ new Date() };
399
+ db.insert(audits).values({
400
+ id: row.id,
401
+ tenantId: row.tenantId,
402
+ projectId: row.projectId,
403
+ url: row.url,
404
+ score: row.score,
405
+ checks: JSON.stringify(row.checks),
406
+ createdAt: row.createdAt
407
+ }).run();
408
+ return row;
409
+ },
410
+ async getById(scope, id) {
411
+ const rows = db.select().from(audits).where(
412
+ and(
413
+ eq(audits.tenantId, scope.tenantId),
414
+ eq(audits.projectId, scope.projectId),
415
+ eq(audits.id, id)
416
+ )
417
+ ).all();
418
+ const row = rows[0];
419
+ if (!row) return void 0;
420
+ return mapAuditRow(row);
421
+ },
422
+ async list(scope) {
423
+ return db.select().from(audits).where(
424
+ and(
425
+ eq(audits.tenantId, scope.tenantId),
426
+ eq(audits.projectId, scope.projectId)
427
+ )
428
+ ).all().map(mapAuditRow);
429
+ }
430
+ },
431
+ reports: {
432
+ async create(input) {
433
+ const row = { ...input, createdAt: /* @__PURE__ */ new Date() };
434
+ db.insert(reports).values({
435
+ id: row.id,
436
+ tenantId: row.tenantId,
437
+ projectId: row.projectId,
438
+ title: row.title,
439
+ from: row.from,
440
+ to: row.to,
441
+ summary: row.summary ? JSON.stringify(row.summary) : null,
442
+ createdAt: row.createdAt
443
+ }).run();
444
+ return row;
445
+ },
446
+ async getById(scope, id) {
447
+ const rows = db.select().from(reports).where(
448
+ and(
449
+ eq(reports.tenantId, scope.tenantId),
450
+ eq(reports.projectId, scope.projectId),
451
+ eq(reports.id, id)
452
+ )
453
+ ).all();
454
+ const row = rows[0];
455
+ if (!row) return void 0;
456
+ return mapReportRow(row);
457
+ },
458
+ async list(scope) {
459
+ return db.select().from(reports).where(
460
+ and(
461
+ eq(reports.tenantId, scope.tenantId),
462
+ eq(reports.projectId, scope.projectId)
463
+ )
464
+ ).all().map(mapReportRow);
465
+ }
466
+ },
467
+ dashboard: {
468
+ async get(scope) {
469
+ const rows = db.select().from(dashboardConfigs).where(
470
+ and(
471
+ eq(dashboardConfigs.tenantId, scope.tenantId),
472
+ eq(dashboardConfigs.projectId, scope.projectId)
473
+ )
474
+ ).all();
475
+ const row = rows[0];
476
+ if (!row) return void 0;
477
+ return {
478
+ id: row.id,
479
+ tenantId: row.tenantId,
480
+ projectId: row.projectId,
481
+ widgets: JSON.parse(row.widgets),
482
+ updatedAt: row.updatedAt
483
+ };
484
+ },
485
+ async upsert(config) {
486
+ db.insert(dashboardConfigs).values({
487
+ id: config.id,
488
+ tenantId: config.tenantId,
489
+ projectId: config.projectId,
490
+ widgets: JSON.stringify(config.widgets),
491
+ updatedAt: config.updatedAt
492
+ }).onConflictDoUpdate({
493
+ target: dashboardConfigs.id,
494
+ set: {
495
+ widgets: JSON.stringify(config.widgets),
496
+ updatedAt: config.updatedAt
497
+ }
498
+ }).run();
499
+ return config;
500
+ }
501
+ },
502
+ blog: {
503
+ async create(input) {
504
+ const now = /* @__PURE__ */ new Date();
505
+ const row = { ...input, createdAt: now, updatedAt: now };
506
+ db.insert(blogPosts).values({
507
+ id: row.id,
508
+ tenantId: row.tenantId,
509
+ projectId: row.projectId,
510
+ title: row.title,
511
+ slug: row.slug,
512
+ content: row.content,
513
+ targetKeyword: row.targetKeyword,
514
+ intent: row.intent,
515
+ metaTitle: row.metaTitle,
516
+ metaDescription: row.metaDescription,
517
+ status: row.status,
518
+ createdAt: row.createdAt,
519
+ updatedAt: row.updatedAt
520
+ }).run();
521
+ return row;
522
+ },
523
+ async getById(scope, id) {
524
+ const rows = db.select().from(blogPosts).where(
525
+ and(
526
+ eq(blogPosts.tenantId, scope.tenantId),
527
+ eq(blogPosts.projectId, scope.projectId),
528
+ eq(blogPosts.id, id)
529
+ )
530
+ ).all();
531
+ const row = rows[0];
532
+ return row ? mapBlogRow(row) : void 0;
533
+ },
534
+ async list(scope) {
535
+ return db.select().from(blogPosts).where(
536
+ and(
537
+ eq(blogPosts.tenantId, scope.tenantId),
538
+ eq(blogPosts.projectId, scope.projectId)
539
+ )
540
+ ).all().map(mapBlogRow);
541
+ },
542
+ async update(scope, id, patch) {
543
+ const rows = db.select().from(blogPosts).where(
544
+ and(
545
+ eq(blogPosts.tenantId, scope.tenantId),
546
+ eq(blogPosts.projectId, scope.projectId),
547
+ eq(blogPosts.id, id)
548
+ )
549
+ ).all();
550
+ const existing = rows[0];
551
+ if (!existing) return void 0;
552
+ const merged = mapBlogRow(existing);
553
+ const next = {
554
+ ...merged,
555
+ ...patch,
556
+ updatedAt: /* @__PURE__ */ new Date()
557
+ };
558
+ db.update(blogPosts).set({
559
+ title: next.title,
560
+ slug: next.slug,
561
+ content: next.content,
562
+ targetKeyword: next.targetKeyword,
563
+ intent: next.intent,
564
+ metaTitle: next.metaTitle,
565
+ metaDescription: next.metaDescription,
566
+ status: next.status,
567
+ updatedAt: next.updatedAt
568
+ }).where(
569
+ and(
570
+ eq(blogPosts.tenantId, scope.tenantId),
571
+ eq(blogPosts.projectId, scope.projectId),
572
+ eq(blogPosts.id, id)
573
+ )
574
+ ).run();
575
+ return next;
576
+ },
577
+ async delete(scope, id) {
578
+ const result = db.delete(blogPosts).where(
579
+ and(
580
+ eq(blogPosts.tenantId, scope.tenantId),
581
+ eq(blogPosts.projectId, scope.projectId),
582
+ eq(blogPosts.id, id)
583
+ )
584
+ ).run();
585
+ return result.changes > 0;
586
+ }
587
+ }
588
+ };
589
+ }
590
+
591
+ // src/factory.ts
592
+ function createStore(databaseUrl) {
593
+ if (databaseUrl.startsWith("postgres") || databaseUrl.startsWith("mysql")) {
594
+ throw new Error(
595
+ "Postgres and MySQL adapters are not implemented in M0. Use sqlite:// or :memory:"
596
+ );
597
+ }
598
+ const path = databaseUrl.startsWith("sqlite://") ? databaseUrl.slice("sqlite://".length) : databaseUrl;
599
+ return createSqliteStore(path);
600
+ }
601
+ export {
602
+ createSqliteStore,
603
+ createStore
604
+ };
605
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/factory.ts","../src/sqlite-store.ts","../src/schema/sqlite.ts"],"sourcesContent":["import \"server-only\";\n\nexport { createStore } from \"./factory.js\";\nexport { createSqliteStore } from \"./sqlite-store.js\";\n","import \"server-only\";\n\nimport { createSqliteStore } from \"./sqlite-store.js\";\n\nexport function createStore(databaseUrl: string) {\n if (\n databaseUrl.startsWith(\"postgres\") ||\n databaseUrl.startsWith(\"mysql\")\n ) {\n throw new Error(\n \"Postgres and MySQL adapters are not implemented in M0. Use sqlite:// or :memory:\",\n );\n }\n\n const path = databaseUrl.startsWith(\"sqlite://\")\n ? databaseUrl.slice(\"sqlite://\".length)\n : databaseUrl;\n\n return createSqliteStore(path);\n}\n","import \"server-only\";\n\nimport { randomUUID } from \"node:crypto\";\nimport Database from \"better-sqlite3\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport { and, eq, gte, lte } from \"drizzle-orm\";\nimport type {\n Audit,\n AuditCheckResult,\n BlogPost,\n CreateKeywordInput,\n CreateRankSnapshotInput,\n DashboardConfig,\n Keyword,\n KeywordIntent,\n Project,\n RankSnapshot,\n RankStore,\n Report,\n ReportSummary,\n SnapshotRangeQuery,\n TenantScope,\n} from \"@rankmyseo/core\";\nimport {\n audits,\n blogPosts,\n dashboardConfigs,\n keywords,\n projects,\n rankSnapshots,\n reports,\n sqliteSchema,\n} from \"./schema/sqlite.js\";\n\nfunction mapBlogRow(row: {\n id: string;\n tenantId: string;\n projectId: string;\n title: string;\n slug: string;\n content: string;\n targetKeyword: string;\n intent: string;\n metaTitle: string;\n metaDescription: string;\n status: string;\n createdAt: Date;\n updatedAt: Date;\n}): BlogPost {\n return {\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n title: row.title,\n slug: row.slug,\n content: row.content,\n targetKeyword: row.targetKeyword,\n intent: row.intent as KeywordIntent,\n metaTitle: row.metaTitle,\n metaDescription: row.metaDescription,\n status: row.status as BlogPost[\"status\"],\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n\nfunction parseChecks(raw: string | null | undefined): AuditCheckResult[] {\n if (!raw) return [];\n try {\n const parsed: unknown = JSON.parse(raw);\n return Array.isArray(parsed) ? (parsed as AuditCheckResult[]) : [];\n } catch {\n return [];\n }\n}\n\nfunction parseSummary(raw: string | null | undefined): ReportSummary | undefined {\n if (!raw) return undefined;\n try {\n return JSON.parse(raw) as ReportSummary;\n } catch {\n return undefined;\n }\n}\n\nfunction mapAuditRow(row: {\n id: string;\n tenantId: string;\n projectId: string;\n url: string;\n score: number;\n checks: string | null;\n createdAt: Date;\n}): Audit {\n return {\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n url: row.url,\n score: row.score,\n checks: parseChecks(row.checks),\n createdAt: row.createdAt,\n };\n}\n\nfunction mapReportRow(row: {\n id: string;\n tenantId: string;\n projectId: string;\n title: string;\n from: Date;\n to: Date;\n summary: string | null;\n createdAt: Date;\n}): Report {\n return {\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n title: row.title,\n from: row.from,\n to: row.to,\n summary: parseSummary(row.summary),\n createdAt: row.createdAt,\n };\n}\n\nfunction parseTags(raw: string): string[] {\n try {\n const parsed: unknown = JSON.parse(raw);\n return Array.isArray(parsed) ? parsed.filter((t): t is string => typeof t === \"string\") : [];\n } catch {\n return [];\n }\n}\n\nfunction migrate(sqlite: Database.Database) {\n sqlite.exec(`\n CREATE TABLE IF NOT EXISTS rms_projects (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL,\n name TEXT NOT NULL,\n domain TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS rms_keywords (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL,\n project_id TEXT NOT NULL,\n text TEXT NOT NULL,\n country TEXT NOT NULL,\n device TEXT NOT NULL,\n tags TEXT NOT NULL,\n created_at INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS rms_rank_snapshots (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL,\n project_id TEXT NOT NULL,\n keyword_id TEXT NOT NULL,\n position INTEGER,\n url TEXT,\n source TEXT NOT NULL,\n device TEXT NOT NULL,\n country TEXT NOT NULL,\n captured_at INTEGER NOT NULL,\n serp_features TEXT\n );\n CREATE TABLE IF NOT EXISTS rms_audits (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL,\n project_id TEXT NOT NULL,\n url TEXT NOT NULL,\n score INTEGER NOT NULL,\n checks TEXT NOT NULL DEFAULT '[]',\n created_at INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS rms_reports (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL,\n project_id TEXT NOT NULL,\n title TEXT NOT NULL,\n \"from\" INTEGER NOT NULL,\n \"to\" INTEGER NOT NULL,\n summary TEXT,\n created_at INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS rms_dashboard_configs (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL,\n project_id TEXT NOT NULL,\n widgets TEXT NOT NULL,\n updated_at INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS rms_blog_posts (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL,\n project_id TEXT NOT NULL,\n title TEXT NOT NULL,\n slug TEXT NOT NULL,\n content TEXT NOT NULL DEFAULT '',\n target_keyword TEXT NOT NULL DEFAULT '',\n intent TEXT NOT NULL DEFAULT 'informational',\n meta_title TEXT NOT NULL DEFAULT '',\n meta_description TEXT NOT NULL DEFAULT '',\n status TEXT NOT NULL DEFAULT 'draft',\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n );\n `);\n try {\n sqlite.exec(`ALTER TABLE rms_audits ADD COLUMN checks TEXT NOT NULL DEFAULT '[]'`);\n } catch {\n /* column exists */\n }\n try {\n sqlite.exec(`ALTER TABLE rms_reports ADD COLUMN summary TEXT`);\n } catch {\n /* column exists */\n }\n}\n\nexport function createSqliteStore(databasePath: string): RankStore {\n const sqlite = new Database(databasePath);\n migrate(sqlite);\n const db = drizzle(sqlite, { schema: sqliteSchema });\n\n const keywordScopeWhere = (scope: TenantScope) =>\n and(\n eq(keywords.tenantId, scope.tenantId),\n eq(keywords.projectId, scope.projectId),\n );\n\n return {\n projects: {\n async create(input) {\n const now = new Date();\n const row: Project = {\n ...input,\n createdAt: now,\n updatedAt: now,\n };\n db.insert(projects)\n .values({\n id: row.id,\n tenantId: row.tenantId,\n name: row.name,\n domain: row.domain,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n })\n .run();\n return row;\n },\n async getById(scope, id) {\n const rows = db\n .select()\n .from(projects)\n .where(and(eq(projects.tenantId, scope.tenantId), eq(projects.id, id)))\n .all();\n const row = rows[0];\n if (!row) return undefined;\n return {\n id: row.id,\n tenantId: row.tenantId,\n name: row.name,\n domain: row.domain,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n },\n async list(scope) {\n return db\n .select()\n .from(projects)\n .where(eq(projects.tenantId, scope.tenantId))\n .all()\n .map((row) => ({\n id: row.id,\n tenantId: row.tenantId,\n name: row.name,\n domain: row.domain,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n }));\n },\n },\n\n keywords: {\n async create(input: CreateKeywordInput) {\n const row: Keyword = {\n id: randomUUID(),\n ...input,\n createdAt: new Date(),\n };\n db.insert(keywords)\n .values({\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n text: row.text,\n country: row.country,\n device: row.device,\n tags: JSON.stringify(row.tags),\n createdAt: row.createdAt,\n })\n .run();\n return row;\n },\n async getById(scope, id) {\n const rows = db\n .select()\n .from(keywords)\n .where(and(keywordScopeWhere(scope), eq(keywords.id, id)))\n .all();\n const row = rows[0];\n if (!row) return undefined;\n return {\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n text: row.text,\n country: row.country,\n device: row.device as Keyword[\"device\"],\n tags: parseTags(row.tags),\n createdAt: row.createdAt,\n };\n },\n async list(scope) {\n return db\n .select()\n .from(keywords)\n .where(keywordScopeWhere(scope))\n .all()\n .map((row) => ({\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n text: row.text,\n country: row.country,\n device: row.device as Keyword[\"device\"],\n tags: parseTags(row.tags),\n createdAt: row.createdAt,\n }));\n },\n async delete(scope, id) {\n const result = db\n .delete(keywords)\n .where(and(keywordScopeWhere(scope), eq(keywords.id, id)))\n .run();\n return result.changes > 0;\n },\n },\n\n snapshots: {\n async append(input: CreateRankSnapshotInput) {\n const row: RankSnapshot = {\n id: randomUUID(),\n ...input,\n };\n db.insert(rankSnapshots)\n .values({\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n keywordId: row.keywordId,\n position: row.position,\n url: row.url,\n source: row.source,\n device: row.device,\n country: row.country,\n capturedAt: row.capturedAt,\n serpFeatures: row.serpFeatures\n ? JSON.stringify(row.serpFeatures)\n : null,\n })\n .run();\n return row;\n },\n async listByRange(query: SnapshotRangeQuery) {\n const conditions = [\n eq(rankSnapshots.tenantId, query.tenantId),\n eq(rankSnapshots.projectId, query.projectId),\n gte(rankSnapshots.capturedAt, query.from),\n lte(rankSnapshots.capturedAt, query.to),\n ];\n if (query.keywordId) {\n conditions.push(eq(rankSnapshots.keywordId, query.keywordId));\n }\n\n return db\n .select()\n .from(rankSnapshots)\n .where(and(...conditions))\n .all()\n .map((row) => ({\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n keywordId: row.keywordId,\n position: row.position,\n url: row.url,\n source: row.source,\n device: row.device as RankSnapshot[\"device\"],\n country: row.country,\n capturedAt: row.capturedAt,\n serpFeatures: row.serpFeatures\n ? (JSON.parse(row.serpFeatures) as Record<string, unknown>)\n : undefined,\n }));\n },\n },\n\n audits: {\n async create(input) {\n const row: Audit = { ...input, checks: input.checks ?? [], createdAt: new Date() };\n db.insert(audits)\n .values({\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n url: row.url,\n score: row.score,\n checks: JSON.stringify(row.checks),\n createdAt: row.createdAt,\n })\n .run();\n return row;\n },\n async getById(scope, id) {\n const rows = db\n .select()\n .from(audits)\n .where(\n and(\n eq(audits.tenantId, scope.tenantId),\n eq(audits.projectId, scope.projectId),\n eq(audits.id, id),\n ),\n )\n .all();\n const row = rows[0];\n if (!row) return undefined;\n return mapAuditRow(row);\n },\n async list(scope) {\n return db\n .select()\n .from(audits)\n .where(\n and(\n eq(audits.tenantId, scope.tenantId),\n eq(audits.projectId, scope.projectId),\n ),\n )\n .all()\n .map(mapAuditRow);\n },\n },\n\n reports: {\n async create(input) {\n const row: Report = { ...input, createdAt: new Date() };\n db.insert(reports)\n .values({\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n title: row.title,\n from: row.from,\n to: row.to,\n summary: row.summary ? JSON.stringify(row.summary) : null,\n createdAt: row.createdAt,\n })\n .run();\n return row;\n },\n async getById(scope, id) {\n const rows = db\n .select()\n .from(reports)\n .where(\n and(\n eq(reports.tenantId, scope.tenantId),\n eq(reports.projectId, scope.projectId),\n eq(reports.id, id),\n ),\n )\n .all();\n const row = rows[0];\n if (!row) return undefined;\n return mapReportRow(row);\n },\n async list(scope) {\n return db\n .select()\n .from(reports)\n .where(\n and(\n eq(reports.tenantId, scope.tenantId),\n eq(reports.projectId, scope.projectId),\n ),\n )\n .all()\n .map(mapReportRow);\n },\n },\n\n dashboard: {\n async get(scope) {\n const rows = db\n .select()\n .from(dashboardConfigs)\n .where(\n and(\n eq(dashboardConfigs.tenantId, scope.tenantId),\n eq(dashboardConfigs.projectId, scope.projectId),\n ),\n )\n .all();\n const row = rows[0];\n if (!row) return undefined;\n return {\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n widgets: JSON.parse(row.widgets) as DashboardConfig[\"widgets\"],\n updatedAt: row.updatedAt,\n };\n },\n async upsert(config) {\n db.insert(dashboardConfigs)\n .values({\n id: config.id,\n tenantId: config.tenantId,\n projectId: config.projectId,\n widgets: JSON.stringify(config.widgets),\n updatedAt: config.updatedAt,\n })\n .onConflictDoUpdate({\n target: dashboardConfigs.id,\n set: {\n widgets: JSON.stringify(config.widgets),\n updatedAt: config.updatedAt,\n },\n })\n .run();\n return config;\n },\n },\n\n blog: {\n async create(input) {\n const now = new Date();\n const row: BlogPost = { ...input, createdAt: now, updatedAt: now };\n db.insert(blogPosts)\n .values({\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n title: row.title,\n slug: row.slug,\n content: row.content,\n targetKeyword: row.targetKeyword,\n intent: row.intent,\n metaTitle: row.metaTitle,\n metaDescription: row.metaDescription,\n status: row.status,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n })\n .run();\n return row;\n },\n async getById(scope, id) {\n const rows = db\n .select()\n .from(blogPosts)\n .where(\n and(\n eq(blogPosts.tenantId, scope.tenantId),\n eq(blogPosts.projectId, scope.projectId),\n eq(blogPosts.id, id),\n ),\n )\n .all();\n const row = rows[0];\n return row ? mapBlogRow(row) : undefined;\n },\n async list(scope) {\n return db\n .select()\n .from(blogPosts)\n .where(\n and(\n eq(blogPosts.tenantId, scope.tenantId),\n eq(blogPosts.projectId, scope.projectId),\n ),\n )\n .all()\n .map(mapBlogRow);\n },\n async update(scope, id, patch) {\n const rows = db\n .select()\n .from(blogPosts)\n .where(\n and(\n eq(blogPosts.tenantId, scope.tenantId),\n eq(blogPosts.projectId, scope.projectId),\n eq(blogPosts.id, id),\n ),\n )\n .all();\n const existing = rows[0];\n if (!existing) return undefined;\n\n const merged = mapBlogRow(existing);\n const next: BlogPost = {\n ...merged,\n ...patch,\n updatedAt: new Date(),\n };\n\n db.update(blogPosts)\n .set({\n title: next.title,\n slug: next.slug,\n content: next.content,\n targetKeyword: next.targetKeyword,\n intent: next.intent,\n metaTitle: next.metaTitle,\n metaDescription: next.metaDescription,\n status: next.status,\n updatedAt: next.updatedAt,\n })\n .where(\n and(\n eq(blogPosts.tenantId, scope.tenantId),\n eq(blogPosts.projectId, scope.projectId),\n eq(blogPosts.id, id),\n ),\n )\n .run();\n\n return next;\n },\n async delete(scope, id) {\n const result = db\n .delete(blogPosts)\n .where(\n and(\n eq(blogPosts.tenantId, scope.tenantId),\n eq(blogPosts.projectId, scope.projectId),\n eq(blogPosts.id, id),\n ),\n )\n .run();\n return result.changes > 0;\n },\n },\n };\n}\n","import { sqliteTable, text, integer } from \"drizzle-orm/sqlite-core\";\n\nexport const projects = sqliteTable(\"rms_projects\", {\n id: text(\"id\").primaryKey(),\n tenantId: text(\"tenant_id\").notNull(),\n name: text(\"name\").notNull(),\n domain: text(\"domain\").notNull(),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" }).notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" }).notNull(),\n});\n\nexport const keywords = sqliteTable(\"rms_keywords\", {\n id: text(\"id\").primaryKey(),\n tenantId: text(\"tenant_id\").notNull(),\n projectId: text(\"project_id\").notNull(),\n text: text(\"text\").notNull(),\n country: text(\"country\").notNull(),\n device: text(\"device\").notNull(),\n tags: text(\"tags\").notNull(),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" }).notNull(),\n});\n\nexport const rankSnapshots = sqliteTable(\"rms_rank_snapshots\", {\n id: text(\"id\").primaryKey(),\n tenantId: text(\"tenant_id\").notNull(),\n projectId: text(\"project_id\").notNull(),\n keywordId: text(\"keyword_id\").notNull(),\n position: integer(\"position\"),\n url: text(\"url\"),\n source: text(\"source\").notNull(),\n device: text(\"device\").notNull(),\n country: text(\"country\").notNull(),\n capturedAt: integer(\"captured_at\", { mode: \"timestamp_ms\" }).notNull(),\n serpFeatures: text(\"serp_features\"),\n});\n\nexport const audits = sqliteTable(\"rms_audits\", {\n id: text(\"id\").primaryKey(),\n tenantId: text(\"tenant_id\").notNull(),\n projectId: text(\"project_id\").notNull(),\n url: text(\"url\").notNull(),\n score: integer(\"score\").notNull(),\n checks: text(\"checks\").notNull().default(\"[]\"),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" }).notNull(),\n});\n\nexport const reports = sqliteTable(\"rms_reports\", {\n id: text(\"id\").primaryKey(),\n tenantId: text(\"tenant_id\").notNull(),\n projectId: text(\"project_id\").notNull(),\n title: text(\"title\").notNull(),\n from: integer(\"from\", { mode: \"timestamp_ms\" }).notNull(),\n to: integer(\"to\", { mode: \"timestamp_ms\" }).notNull(),\n summary: text(\"summary\"),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" }).notNull(),\n});\n\nexport const dashboardConfigs = sqliteTable(\"rms_dashboard_configs\", {\n id: text(\"id\").primaryKey(),\n tenantId: text(\"tenant_id\").notNull(),\n projectId: text(\"project_id\").notNull(),\n widgets: text(\"widgets\").notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" }).notNull(),\n});\n\nexport const blogPosts = sqliteTable(\"rms_blog_posts\", {\n id: text(\"id\").primaryKey(),\n tenantId: text(\"tenant_id\").notNull(),\n projectId: text(\"project_id\").notNull(),\n title: text(\"title\").notNull(),\n slug: text(\"slug\").notNull(),\n content: text(\"content\").notNull().default(\"\"),\n targetKeyword: text(\"target_keyword\").notNull().default(\"\"),\n intent: text(\"intent\").notNull().default(\"informational\"),\n metaTitle: text(\"meta_title\").notNull().default(\"\"),\n metaDescription: text(\"meta_description\").notNull().default(\"\"),\n status: text(\"status\").notNull().default(\"draft\"),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" }).notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" }).notNull(),\n});\n\nexport const sqliteSchema = {\n projects,\n keywords,\n rankSnapshots,\n audits,\n reports,\n dashboardConfigs,\n blogPosts,\n};\n"],"mappings":"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","names":[]}