nothing-browser 0.1.2 → 0.1.4
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/package.json +14 -26
- package/piggy/cache/{memory.ts → memory.js} +5 -10
- package/piggy/captcha/{index.ts → index.js} +19 -28
- package/piggy/capture/index.js +40 -0
- package/piggy/client/{index.ts → index.js} +133 -154
- package/piggy/dialog/{index.ts → index.js} +15 -31
- package/piggy/export/index.js +105 -0
- package/piggy/expose/index.js +24 -0
- package/piggy/find/{index.ts → index.js} +24 -39
- package/piggy/http/{index.ts → index.js} +7 -16
- package/piggy/human/{index.ts → index.js} +18 -49
- package/piggy/iframe/index.js +56 -0
- package/piggy/interactions/{index.ts → index.js} +20 -23
- package/piggy/intercept/{scripts.ts → scripts.js} +4 -13
- package/piggy/launch/{detect.ts → detect.js} +4 -6
- package/piggy/launch/{spawn.ts → spawn.js} +17 -22
- package/piggy/media/{index.ts → index.js} +13 -11
- package/piggy/navigation/{index.ts → index.js} +16 -14
- package/piggy/pool/index.js +76 -0
- package/piggy/provide/index.js +97 -0
- package/piggy/proxy/index.js +95 -0
- package/piggy/register/index.js +543 -0
- package/piggy/router/index.js +44 -0
- package/piggy/server/{index.ts → index.js} +16 -64
- package/piggy/session/index.js +69 -0
- package/piggy/store/{index.ts → index.js} +22 -62
- package/piggy/tabs/index.js +24 -0
- package/piggy/wait/index.js +77 -0
- package/piggy.js +316 -0
- package/dist/cache/memory.js +0 -35
- package/dist/client/index.js +0 -1284
- package/dist/human/index.js +0 -82
- package/dist/launch/detect.js +0 -782
- package/dist/launch/spawn.js +0 -915
- package/dist/logger/index.js +0 -733
- package/dist/piggy/cache/memory.d.ts +0 -7
- package/dist/piggy/cache/memory.d.ts.map +0 -1
- package/dist/piggy/captcha/index.d.ts +0 -39
- package/dist/piggy/captcha/index.d.ts.map +0 -1
- package/dist/piggy/capture/index.d.ts +0 -48
- package/dist/piggy/capture/index.d.ts.map +0 -1
- package/dist/piggy/client/index.d.ts +0 -146
- package/dist/piggy/client/index.d.ts.map +0 -1
- package/dist/piggy/dialog/index.d.ts +0 -28
- package/dist/piggy/dialog/index.d.ts.map +0 -1
- package/dist/piggy/export/index.d.ts +0 -62
- package/dist/piggy/export/index.d.ts.map +0 -1
- package/dist/piggy/expose/index.d.ts +0 -6
- package/dist/piggy/expose/index.d.ts.map +0 -1
- package/dist/piggy/find/index.d.ts +0 -52
- package/dist/piggy/find/index.d.ts.map +0 -1
- package/dist/piggy/http/index.d.ts +0 -14
- package/dist/piggy/http/index.d.ts.map +0 -1
- package/dist/piggy/human/index.d.ts +0 -39
- package/dist/piggy/human/index.d.ts.map +0 -1
- package/dist/piggy/iframe/index.d.ts +0 -53
- package/dist/piggy/iframe/index.d.ts.map +0 -1
- package/dist/piggy/interactions/index.d.ts +0 -24
- package/dist/piggy/interactions/index.d.ts.map +0 -1
- package/dist/piggy/intercept/scripts.d.ts +0 -13
- package/dist/piggy/intercept/scripts.d.ts.map +0 -1
- package/dist/piggy/launch/detect.d.ts +0 -3
- package/dist/piggy/launch/detect.d.ts.map +0 -1
- package/dist/piggy/launch/spawn.d.ts +0 -6
- package/dist/piggy/launch/spawn.d.ts.map +0 -1
- package/dist/piggy/logger/index.d.ts +0 -3
- package/dist/piggy/logger/index.d.ts.map +0 -1
- package/dist/piggy/media/index.d.ts +0 -11
- package/dist/piggy/media/index.d.ts.map +0 -1
- package/dist/piggy/navigation/index.d.ts +0 -16
- package/dist/piggy/navigation/index.d.ts.map +0 -1
- package/dist/piggy/pool/index.d.ts +0 -23
- package/dist/piggy/pool/index.d.ts.map +0 -1
- package/dist/piggy/provide/index.d.ts +0 -98
- package/dist/piggy/provide/index.d.ts.map +0 -1
- package/dist/piggy/proxy/index.d.ts +0 -94
- package/dist/piggy/proxy/index.d.ts.map +0 -1
- package/dist/piggy/register/index.d.ts +0 -9
- package/dist/piggy/register/index.d.ts.map +0 -1
- package/dist/piggy/router/index.d.ts +0 -38
- package/dist/piggy/router/index.d.ts.map +0 -1
- package/dist/piggy/server/index.d.ts +0 -42
- package/dist/piggy/server/index.d.ts.map +0 -1
- package/dist/piggy/session/index.d.ts +0 -27
- package/dist/piggy/session/index.d.ts.map +0 -1
- package/dist/piggy/store/index.d.ts +0 -22
- package/dist/piggy/store/index.d.ts.map +0 -1
- package/dist/piggy/tabs/index.d.ts +0 -12
- package/dist/piggy/tabs/index.d.ts.map +0 -1
- package/dist/piggy/wait/index.d.ts +0 -28
- package/dist/piggy/wait/index.d.ts.map +0 -1
- package/dist/piggy.d.ts +0 -10
- package/dist/piggy.d.ts.map +0 -1
- package/dist/piggy.js +0 -30186
- package/dist/register/index.js +0 -23710
- package/dist/server/index.js +0 -26831
- package/piggy/capture/index.ts +0 -76
- package/piggy/export/index.d.ts +0 -117
- package/piggy/export/index.ts +0 -147
- package/piggy/expose/index.ts +0 -42
- package/piggy/iframe/index.ts +0 -79
- package/piggy/intercept/scripts.d.ts +0 -13
- package/piggy/intercept/scripts.d.ts.map +0 -1
- package/piggy/launch/detect.d.ts +0 -3
- package/piggy/launch/detect.d.ts.map +0 -1
- package/piggy/launch/spawn.d.ts +0 -6
- package/piggy/launch/spawn.d.ts.map +0 -1
- package/piggy/logger/index.d.ts +0 -3
- package/piggy/logger/index.d.ts.map +0 -1
- package/piggy/pool/index.d.ts +0 -12
- package/piggy/pool/index.ts +0 -75
- package/piggy/provide/index.ts +0 -170
- package/piggy/proxy/index.ts +0 -154
- package/piggy/register/index.ts +0 -575
- package/piggy/router/index.ts +0 -69
- package/piggy/session/index.ts +0 -76
- package/piggy/store/index.d.ts +0 -26
- package/piggy/tabs/index.ts +0 -23
- package/piggy/wait/index.ts +0 -90
- package/piggy.ts +0 -274
- /package/piggy/logger/{index.ts → index.js} +0 -0
|
@@ -1,55 +1,15 @@
|
|
|
1
|
-
// piggy/server/index.
|
|
1
|
+
// piggy/server/index.js
|
|
2
2
|
import { Elysia } from "elysia";
|
|
3
3
|
import { openapi } from "@elysiajs/openapi";
|
|
4
|
-
import * as cache from "../cache/memory";
|
|
5
|
-
import logger from "../logger";
|
|
6
|
-
|
|
7
|
-
export type BeforeMiddleware = (ctx: {
|
|
8
|
-
params: Record<string, string>;
|
|
9
|
-
query: Record<string, string>;
|
|
10
|
-
body: any;
|
|
11
|
-
headers: Record<string, string>;
|
|
12
|
-
set: any;
|
|
13
|
-
}) => void | Promise<void>;
|
|
14
|
-
|
|
15
|
-
export type RouteHandler = (
|
|
16
|
-
params: Record<string, string>,
|
|
17
|
-
query: Record<string, string>,
|
|
18
|
-
body: any
|
|
19
|
-
) => Promise<any>;
|
|
20
|
-
|
|
21
|
-
export interface RouteParameter {
|
|
22
|
-
name: string;
|
|
23
|
-
in: "query" | "path" | "header" | "cookie";
|
|
24
|
-
description?: string;
|
|
25
|
-
required?: boolean;
|
|
26
|
-
schema?: Record<string, any>;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface RouteDetail {
|
|
30
|
-
tags?: string[];
|
|
31
|
-
summary?: string;
|
|
32
|
-
description?: string;
|
|
33
|
-
deprecated?: boolean;
|
|
34
|
-
hide?: boolean;
|
|
35
|
-
parameters?: RouteParameter[];
|
|
36
|
-
}
|
|
4
|
+
import * as cache from "../cache/memory.js";
|
|
5
|
+
import logger from "../logger.js";
|
|
37
6
|
|
|
38
|
-
export
|
|
39
|
-
|
|
40
|
-
method: "GET" | "POST" | "PUT" | "DELETE";
|
|
41
|
-
handler: RouteHandler;
|
|
42
|
-
ttl: number;
|
|
43
|
-
before: BeforeMiddleware[];
|
|
44
|
-
detail?: RouteDetail;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export const routeRegistry = new Map<string, RouteConfig>();
|
|
48
|
-
export const keepAliveSites = new Set<string>();
|
|
7
|
+
export const routeRegistry = new Map();
|
|
8
|
+
export const keepAliveSites = new Set();
|
|
49
9
|
|
|
50
10
|
// ── Error mapper ──────────────────────────────────────────────────────────────
|
|
51
11
|
|
|
52
|
-
function mapError(err
|
|
12
|
+
function mapError(err, site) {
|
|
53
13
|
const msg = err.message.toLowerCase();
|
|
54
14
|
if (msg.includes("selector") || msg.includes("not found") || msg.includes("null"))
|
|
55
15
|
return { status: 404, error: "Not found", site, details: err.message };
|
|
@@ -62,18 +22,9 @@ function mapError(err: Error, site: string) {
|
|
|
62
22
|
|
|
63
23
|
// ── Server factory ────────────────────────────────────────────────────────────
|
|
64
24
|
|
|
65
|
-
let _app
|
|
25
|
+
let _app = null;
|
|
66
26
|
|
|
67
|
-
export async function startServer(
|
|
68
|
-
port: number,
|
|
69
|
-
hostname = "0.0.0.0",
|
|
70
|
-
openapiOpts?: {
|
|
71
|
-
title?: string;
|
|
72
|
-
version?: string;
|
|
73
|
-
description?: string;
|
|
74
|
-
path?: string;
|
|
75
|
-
}
|
|
76
|
-
): Promise<Elysia> {
|
|
27
|
+
export async function startServer(port, hostname = "0.0.0.0", openapiOpts) {
|
|
77
28
|
_app = new Elysia();
|
|
78
29
|
|
|
79
30
|
// ── OpenAPI ───────────────────────────────────────────────────────────────
|
|
@@ -117,15 +68,15 @@ export async function startServer(
|
|
|
117
68
|
const colonIdx = registryKey.indexOf(":");
|
|
118
69
|
const siteName = registryKey.substring(0, colonIdx);
|
|
119
70
|
const fullPath = `/${siteName}${config.path}`;
|
|
120
|
-
const method = config.method.toLowerCase()
|
|
71
|
+
const method = config.method.toLowerCase();
|
|
121
72
|
|
|
122
73
|
logger.info(`[server] mounting ${config.method} ${fullPath} (ttl=${config.ttl}ms)`);
|
|
123
74
|
|
|
124
|
-
const routeHandler = async ({ params, query, body, headers, set }
|
|
75
|
+
const routeHandler = async ({ params, query, body, headers, set }) => {
|
|
125
76
|
for (const mw of config.before) {
|
|
126
77
|
try {
|
|
127
78
|
await mw({ params, query, body, headers, set });
|
|
128
|
-
} catch (e
|
|
79
|
+
} catch (e) {
|
|
129
80
|
set.status = set.status ?? 400;
|
|
130
81
|
return { error: e.message };
|
|
131
82
|
}
|
|
@@ -139,10 +90,10 @@ export async function startServer(
|
|
|
139
90
|
return hit;
|
|
140
91
|
}
|
|
141
92
|
|
|
142
|
-
let result
|
|
93
|
+
let result;
|
|
143
94
|
try {
|
|
144
95
|
result = await config.handler(params, query, body);
|
|
145
|
-
} catch (e
|
|
96
|
+
} catch (e) {
|
|
146
97
|
const mapped = mapError(e, siteName);
|
|
147
98
|
set.status = mapped.status;
|
|
148
99
|
return mapped;
|
|
@@ -173,8 +124,9 @@ export async function startServer(
|
|
|
173
124
|
|
|
174
125
|
_app.listen({ port, hostname });
|
|
175
126
|
|
|
176
|
-
|
|
177
|
-
logger.success(
|
|
127
|
+
const host = hostname === "0.0.0.0" ? "localhost" : hostname;
|
|
128
|
+
logger.success(`🚀 Piggy API server → http://${host}:${port}`);
|
|
129
|
+
logger.success(`📖 OpenAPI docs → http://${host}:${port}/openapi`);
|
|
178
130
|
logger.info(` Routes mounted: ${routeRegistry.size}`);
|
|
179
131
|
routeRegistry.forEach((cfg, key) => {
|
|
180
132
|
const siteName = key.substring(0, key.indexOf(":"));
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// piggy/session/index.js
|
|
2
|
+
import { PiggyClient } from "../client.js";
|
|
3
|
+
|
|
4
|
+
export class SessionClient {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Session lifecycle
|
|
10
|
+
reload(tabId = "default") {
|
|
11
|
+
return this.client.send("session.reload", { tabId });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Paths
|
|
15
|
+
paths() {
|
|
16
|
+
return this.client.send("session.paths", {});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
cookiesPath() {
|
|
20
|
+
return this.client.send("session.cookies.path", {});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
profilePath() {
|
|
24
|
+
return this.client.send("session.profile.path", {});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
wsPath() {
|
|
28
|
+
return this.client.send("session.ws.path", {});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
pingsPath() {
|
|
32
|
+
return this.client.send("session.pings.path", {});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Opt-in persistence
|
|
36
|
+
setWsSave(enabled) {
|
|
37
|
+
return this.client.send("session.ws.save", { enabled });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
setPingsSave(enabled) {
|
|
41
|
+
return this.client.send("session.pings.save", { enabled });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Export / import
|
|
45
|
+
async export(tabId = "default") {
|
|
46
|
+
const raw = await this.client.send("session.export", { tabId });
|
|
47
|
+
return typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
import(data, tabId = "default") {
|
|
51
|
+
return this.client.send("session.import", {
|
|
52
|
+
data: JSON.stringify(data),
|
|
53
|
+
tabId,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Cookies
|
|
58
|
+
setCookie(opts, tabId = "default") {
|
|
59
|
+
return this.client.send("cookie.set", { ...opts, tabId });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
deleteCookie(opts, tabId = "default") {
|
|
63
|
+
return this.client.send("cookie.delete", { ...opts, tabId });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function createSessionAPI(client) {
|
|
68
|
+
return new SessionClient(client);
|
|
69
|
+
}
|
|
@@ -1,33 +1,13 @@
|
|
|
1
|
-
// piggy/store/index.
|
|
1
|
+
// piggy/store/index.js
|
|
2
2
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
3
3
|
import { dirname, resolve } from "path";
|
|
4
|
-
import logger from "../logger";
|
|
5
|
-
|
|
6
|
-
// ── Types ─────────────────────────────────────────────────────────────────────
|
|
7
|
-
|
|
8
|
-
export type FieldType = "string" | "number" | "boolean" | "object" | "array";
|
|
9
|
-
|
|
10
|
-
export interface FieldSchema {
|
|
11
|
-
type: FieldType;
|
|
12
|
-
required?: boolean; // default false — missing = NULL not error
|
|
13
|
-
default?: any; // fallback if missing (overrides NULL)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface StoreSchema {
|
|
17
|
-
name: string; // store identifier (matches site name or custom)
|
|
18
|
-
destination: string; // "./data.json" or "./data.db"
|
|
19
|
-
fields: Record<string, FieldSchema>;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface PiggyStoreConfig {
|
|
23
|
-
stores: StoreSchema[];
|
|
24
|
-
}
|
|
4
|
+
import logger from "../logger.js";
|
|
25
5
|
|
|
26
6
|
// ── Load piggy.store.json ─────────────────────────────────────────────────────
|
|
27
7
|
|
|
28
|
-
let _config
|
|
8
|
+
let _config = null;
|
|
29
9
|
|
|
30
|
-
export function loadStoreConfig(configPath = "./piggy.store.json")
|
|
10
|
+
export function loadStoreConfig(configPath = "./piggy.store.json") {
|
|
31
11
|
if (_config) return _config;
|
|
32
12
|
|
|
33
13
|
const abs = resolve(configPath);
|
|
@@ -37,39 +17,33 @@ export function loadStoreConfig(configPath = "./piggy.store.json"): PiggyStoreCo
|
|
|
37
17
|
}
|
|
38
18
|
|
|
39
19
|
try {
|
|
40
|
-
_config = JSON.parse(readFileSync(abs, "utf8"))
|
|
20
|
+
_config = JSON.parse(readFileSync(abs, "utf8"));
|
|
41
21
|
logger.success(`[store] loaded ${_config.stores.length} schema(s) from ${abs}`);
|
|
42
22
|
return _config;
|
|
43
|
-
} catch (e
|
|
23
|
+
} catch (e) {
|
|
44
24
|
logger.error(`[store] failed to parse piggy.store.json: ${e.message}`);
|
|
45
25
|
return { stores: [] };
|
|
46
26
|
}
|
|
47
27
|
}
|
|
48
28
|
|
|
49
|
-
export function getSchema(storeName
|
|
29
|
+
export function getSchema(storeName) {
|
|
50
30
|
const config = loadStoreConfig();
|
|
51
31
|
return config.stores.find(s => s.name === storeName) ?? null;
|
|
52
32
|
}
|
|
53
33
|
|
|
54
34
|
// ── Validate & shape one record against schema ────────────────────────────────
|
|
55
35
|
|
|
56
|
-
export function shapeRecord(data
|
|
57
|
-
const shaped
|
|
36
|
+
export function shapeRecord(data, schema) {
|
|
37
|
+
const shaped = {};
|
|
58
38
|
|
|
59
39
|
for (const [field, def] of Object.entries(schema.fields)) {
|
|
60
40
|
const raw = data[field];
|
|
61
41
|
|
|
62
|
-
// Missing field
|
|
63
42
|
if (raw === undefined || raw === null) {
|
|
64
|
-
|
|
65
|
-
shaped[field] = def.default;
|
|
66
|
-
} else {
|
|
67
|
-
shaped[field] = null; // NULL — not an error, just absent
|
|
68
|
-
}
|
|
43
|
+
shaped[field] = def.default !== undefined ? def.default : null;
|
|
69
44
|
continue;
|
|
70
45
|
}
|
|
71
46
|
|
|
72
|
-
// Type coercion / validation
|
|
73
47
|
switch (def.type) {
|
|
74
48
|
case "string":
|
|
75
49
|
shaped[field] = String(raw);
|
|
@@ -92,17 +66,16 @@ export function shapeRecord(data: Record<string, any>, schema: StoreSchema): Rec
|
|
|
92
66
|
}
|
|
93
67
|
}
|
|
94
68
|
|
|
95
|
-
// Extra fields on incoming data are silently dropped — not in schema = ignored
|
|
96
69
|
return shaped;
|
|
97
70
|
}
|
|
98
71
|
|
|
99
72
|
// ── JSON backend ──────────────────────────────────────────────────────────────
|
|
100
73
|
|
|
101
|
-
function appendToJson(destination
|
|
74
|
+
function appendToJson(destination, record) {
|
|
102
75
|
const abs = resolve(destination);
|
|
103
76
|
mkdirSync(dirname(abs), { recursive: true });
|
|
104
77
|
|
|
105
|
-
let existing
|
|
78
|
+
let existing = [];
|
|
106
79
|
if (existsSync(abs)) {
|
|
107
80
|
try {
|
|
108
81
|
existing = JSON.parse(readFileSync(abs, "utf8"));
|
|
@@ -118,15 +91,14 @@ function appendToJson(destination: string, record: Record<string, any>): void {
|
|
|
118
91
|
|
|
119
92
|
// ── SQLite backend ────────────────────────────────────────────────────────────
|
|
120
93
|
|
|
121
|
-
function appendToSqlite(destination
|
|
122
|
-
// Use bun:sqlite if available, else require better-sqlite3
|
|
94
|
+
function appendToSqlite(destination, record, schema) {
|
|
123
95
|
const abs = resolve(destination);
|
|
124
96
|
mkdirSync(dirname(abs), { recursive: true });
|
|
125
97
|
|
|
126
|
-
const isBun = typeof
|
|
98
|
+
const isBun = typeof globalThis.Bun !== "undefined";
|
|
127
99
|
|
|
128
100
|
if (isBun) {
|
|
129
|
-
const { Database } = require("bun:sqlite")
|
|
101
|
+
const { Database } = require("bun:sqlite");
|
|
130
102
|
const db = new Database(abs);
|
|
131
103
|
ensureTable(db, schema, "bun");
|
|
132
104
|
insertRecord(db, schema, record, "bun");
|
|
@@ -140,12 +112,12 @@ function appendToSqlite(destination: string, record: Record<string, any>, schema
|
|
|
140
112
|
}
|
|
141
113
|
}
|
|
142
114
|
|
|
143
|
-
function ensureTable(db
|
|
115
|
+
function ensureTable(db, schema, runtime) {
|
|
144
116
|
const tableName = schema.name.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
145
117
|
const cols = Object.entries(schema.fields).map(([name, def]) => {
|
|
146
118
|
const sqlType = def.type === "number" ? "REAL"
|
|
147
119
|
: def.type === "boolean" ? "INTEGER"
|
|
148
|
-
: def.type === "object" || def.type === "array" ? "TEXT"
|
|
120
|
+
: def.type === "object" || def.type === "array" ? "TEXT"
|
|
149
121
|
: "TEXT";
|
|
150
122
|
return ` ${name} ${sqlType}`;
|
|
151
123
|
});
|
|
@@ -153,20 +125,15 @@ function ensureTable(db: any, schema: StoreSchema, runtime: "bun" | "node"): voi
|
|
|
153
125
|
|
|
154
126
|
const sql = `CREATE TABLE IF NOT EXISTS ${tableName} (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n${cols.join(",\n")}\n)`;
|
|
155
127
|
|
|
156
|
-
|
|
157
|
-
db.run(sql);
|
|
158
|
-
} else {
|
|
159
|
-
db.prepare(sql).run();
|
|
160
|
-
}
|
|
128
|
+
runtime === "bun" ? db.run(sql) : db.prepare(sql).run();
|
|
161
129
|
}
|
|
162
130
|
|
|
163
|
-
function insertRecord(db
|
|
131
|
+
function insertRecord(db, schema, record, runtime) {
|
|
164
132
|
const tableName = schema.name.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
165
133
|
const fields = [...Object.keys(schema.fields), "_storedAt"];
|
|
166
134
|
const values = fields.map(f => {
|
|
167
135
|
if (f === "_storedAt") return new Date().toISOString();
|
|
168
136
|
const v = record[f];
|
|
169
|
-
// Serialize objects/arrays as JSON strings for SQLite
|
|
170
137
|
if (v !== null && (typeof v === "object" || Array.isArray(v))) return JSON.stringify(v);
|
|
171
138
|
return v ?? null;
|
|
172
139
|
});
|
|
@@ -177,19 +144,12 @@ function insertRecord(db: any, schema: StoreSchema, record: Record<string, any>,
|
|
|
177
144
|
|
|
178
145
|
const sql = `INSERT INTO ${tableName} (${fields.join(", ")}) VALUES (${placeholders})`;
|
|
179
146
|
|
|
180
|
-
|
|
181
|
-
db.run(sql, values);
|
|
182
|
-
} else {
|
|
183
|
-
db.prepare(sql).run(values);
|
|
184
|
-
}
|
|
147
|
+
runtime === "bun" ? db.run(sql, values) : db.prepare(sql).run(values);
|
|
185
148
|
}
|
|
186
149
|
|
|
187
150
|
// ── Main store function ───────────────────────────────────────────────────────
|
|
188
151
|
|
|
189
|
-
export async function storeRecord(
|
|
190
|
-
storeName: string,
|
|
191
|
-
data: Record<string, any> | Record<string, any>[]
|
|
192
|
-
): Promise<{ stored: number; skipped: number }> {
|
|
152
|
+
export async function storeRecord(storeName, data) {
|
|
193
153
|
const schema = getSchema(storeName);
|
|
194
154
|
|
|
195
155
|
if (!schema) {
|
|
@@ -220,7 +180,7 @@ export async function storeRecord(
|
|
|
220
180
|
|
|
221
181
|
stored++;
|
|
222
182
|
logger.success(`[store][${storeName}] stored record → ${schema.destination}`);
|
|
223
|
-
} catch (e
|
|
183
|
+
} catch (e) {
|
|
224
184
|
logger.error(`[store][${storeName}] failed to store record: ${e.message}`);
|
|
225
185
|
skipped++;
|
|
226
186
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// piggy/tabs/index.js
|
|
2
|
+
|
|
3
|
+
export class TabsClient {
|
|
4
|
+
constructor(client) {
|
|
5
|
+
this.client = client;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
new() {
|
|
9
|
+
return this.client.send("tab.new", {});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
close(opts) {
|
|
13
|
+
const tabId = typeof opts === "string" ? opts : opts.tabId;
|
|
14
|
+
return this.client.send("tab.close", { tabId });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
list() {
|
|
18
|
+
return this.client.send("tab.list", {});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function createTabsAPI(client) {
|
|
23
|
+
return new TabsClient(client);
|
|
24
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ─── WaitClient ───────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
class WaitClient {
|
|
6
|
+
constructor(client) {
|
|
7
|
+
this.client = client;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function(js, timeout = 10000, tabId = "default") {
|
|
11
|
+
return this.client.send("wait.function", { js, timeout, tabId });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
selector(selector, state = "attached", timeout = 10000, tabId = "default") {
|
|
15
|
+
return this.client.send("wait.selector", { selector, state, timeout, tabId });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ─── EvaluateClient ───────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
class EvaluateClient {
|
|
22
|
+
constructor(client) {
|
|
23
|
+
this.client = client;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
run(js, timeout, tabId = "default") {
|
|
27
|
+
return this.client.send("evaluate", {
|
|
28
|
+
js,
|
|
29
|
+
tabId,
|
|
30
|
+
...(timeout !== undefined ? { timeout } : {}),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ─── FetchClient ──────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
class FetchClient {
|
|
38
|
+
constructor(client) {
|
|
39
|
+
this.client = client;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
text(selector, tabId = "default") {
|
|
43
|
+
return this.client.send("fetch.text", { query: selector, tabId });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
textAll(selector, tabId = "default") {
|
|
47
|
+
return this.client.send("fetch.textAll", { selector, tabId });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
attr(selector, attr, tabId = "default") {
|
|
51
|
+
return this.client.send("fetch.attr", { selector, attr, tabId });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
attrAll(selector, attr, tabId = "default") {
|
|
55
|
+
return this.client.send("fetch.attrAll", { selector, attr, tabId });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
links(selector, tabId = "default") {
|
|
59
|
+
return this.client.send("fetch.links", { query: selector, tabId });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
linksAll(tabId = "default") {
|
|
63
|
+
return this.client.send("fetch.links.all", { tabId });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
images(selector, tabId = "default") {
|
|
67
|
+
return this.client.send("fetch.image", { query: selector, tabId });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ─── Factories ────────────────────────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
function createWaitAPI(client) { return new WaitClient(client); }
|
|
74
|
+
function createEvaluateAPI(client) { return new EvaluateClient(client); }
|
|
75
|
+
function createFetchAPI(client) { return new FetchClient(client); }
|
|
76
|
+
|
|
77
|
+
module.exports = { WaitClient, EvaluateClient, FetchClient, createWaitAPI, createEvaluateAPI, createFetchAPI };
|