offcourse 0.0.2 → 1.0.1
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/README.md +255 -20
- package/dist/cli/commands/config.d.ts +13 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +66 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/inspect.d.ts +11 -0
- package/dist/cli/commands/inspect.d.ts.map +1 -0
- package/dist/cli/commands/inspect.js +365 -0
- package/dist/cli/commands/inspect.js.map +1 -0
- package/dist/cli/commands/login.d.ts +12 -0
- package/dist/cli/commands/login.d.ts.map +1 -0
- package/dist/cli/commands/login.js +55 -0
- package/dist/cli/commands/login.js.map +1 -0
- package/dist/cli/commands/status.d.ts +15 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +118 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/sync.d.ts +15 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +921 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/commands/syncHighLevel.d.ts +23 -0
- package/dist/cli/commands/syncHighLevel.d.ts.map +1 -0
- package/dist/cli/commands/syncHighLevel.js +479 -0
- package/dist/cli/commands/syncHighLevel.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +106 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/configManager.d.ts +31 -0
- package/dist/config/configManager.d.ts.map +1 -0
- package/dist/config/configManager.js +68 -0
- package/dist/config/configManager.js.map +1 -0
- package/dist/config/paths.d.ts +21 -0
- package/dist/config/paths.d.ts.map +1 -0
- package/dist/config/paths.js +33 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/schema.d.ts +60 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +50 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/downloader/hlsDownloader.d.ts +58 -0
- package/dist/downloader/hlsDownloader.d.ts.map +1 -0
- package/dist/downloader/hlsDownloader.js +263 -0
- package/dist/downloader/hlsDownloader.js.map +1 -0
- package/dist/downloader/hlsValidator.d.ts +35 -0
- package/dist/downloader/hlsValidator.d.ts.map +1 -0
- package/dist/downloader/hlsValidator.js +152 -0
- package/dist/downloader/hlsValidator.js.map +1 -0
- package/dist/downloader/index.d.ts +29 -0
- package/dist/downloader/index.d.ts.map +1 -0
- package/dist/downloader/index.js +55 -0
- package/dist/downloader/index.js.map +1 -0
- package/dist/downloader/loomDownloader.d.ts +56 -0
- package/dist/downloader/loomDownloader.d.ts.map +1 -0
- package/dist/downloader/loomDownloader.js +562 -0
- package/dist/downloader/loomDownloader.js.map +1 -0
- package/dist/downloader/queue.d.ts +56 -0
- package/dist/downloader/queue.d.ts.map +1 -0
- package/dist/downloader/queue.js +88 -0
- package/dist/downloader/queue.js.map +1 -0
- package/dist/downloader/vimeoDownloader.d.ts +52 -0
- package/dist/downloader/vimeoDownloader.d.ts.map +1 -0
- package/dist/downloader/vimeoDownloader.js +569 -0
- package/dist/downloader/vimeoDownloader.js.map +1 -0
- package/dist/scraper/extractor.d.ts +53 -0
- package/dist/scraper/extractor.d.ts.map +1 -0
- package/dist/scraper/extractor.js +627 -0
- package/dist/scraper/extractor.js.map +1 -0
- package/dist/scraper/highlevel/extractor.d.ts +89 -0
- package/dist/scraper/highlevel/extractor.d.ts.map +1 -0
- package/dist/scraper/highlevel/extractor.js +373 -0
- package/dist/scraper/highlevel/extractor.js.map +1 -0
- package/dist/scraper/highlevel/index.d.ts +3 -0
- package/dist/scraper/highlevel/index.d.ts.map +1 -0
- package/dist/scraper/highlevel/index.js +3 -0
- package/dist/scraper/highlevel/index.js.map +1 -0
- package/dist/scraper/highlevel/navigator.d.ts +86 -0
- package/dist/scraper/highlevel/navigator.d.ts.map +1 -0
- package/dist/scraper/highlevel/navigator.js +505 -0
- package/dist/scraper/highlevel/navigator.js.map +1 -0
- package/dist/scraper/highlevel/schemas.d.ts +188 -0
- package/dist/scraper/highlevel/schemas.d.ts.map +1 -0
- package/dist/scraper/highlevel/schemas.js +139 -0
- package/dist/scraper/highlevel/schemas.js.map +1 -0
- package/dist/scraper/navigator.d.ts +68 -0
- package/dist/scraper/navigator.d.ts.map +1 -0
- package/dist/scraper/navigator.js +257 -0
- package/dist/scraper/navigator.js.map +1 -0
- package/dist/scraper/schemas.d.ts +57 -0
- package/dist/scraper/schemas.d.ts.map +1 -0
- package/dist/scraper/schemas.js +135 -0
- package/dist/scraper/schemas.js.map +1 -0
- package/dist/scraper/videoInterceptor.d.ts +23 -0
- package/dist/scraper/videoInterceptor.d.ts.map +1 -0
- package/dist/scraper/videoInterceptor.js +330 -0
- package/dist/scraper/videoInterceptor.js.map +1 -0
- package/dist/shared/auth.d.ts +58 -0
- package/dist/shared/auth.d.ts.map +1 -0
- package/dist/shared/auth.js +197 -0
- package/dist/shared/auth.js.map +1 -0
- package/dist/shared/firebase.d.ts +60 -0
- package/dist/shared/firebase.d.ts.map +1 -0
- package/dist/shared/firebase.js +102 -0
- package/dist/shared/firebase.js.map +1 -0
- package/dist/shared/fs.d.ts +31 -0
- package/dist/shared/fs.d.ts.map +1 -0
- package/dist/shared/fs.js +77 -0
- package/dist/shared/fs.js.map +1 -0
- package/dist/shared/http.d.ts +15 -0
- package/dist/shared/http.d.ts.map +1 -0
- package/dist/shared/http.js +31 -0
- package/dist/shared/http.js.map +1 -0
- package/dist/shared/index.d.ts +7 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +7 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/slug.d.ts +11 -0
- package/dist/shared/slug.d.ts.map +1 -0
- package/dist/shared/slug.js +25 -0
- package/dist/shared/slug.js.map +1 -0
- package/dist/shared/url.d.ts +43 -0
- package/dist/shared/url.d.ts.map +1 -0
- package/dist/shared/url.js +54 -0
- package/dist/shared/url.js.map +1 -0
- package/dist/state/database.d.ts +246 -0
- package/dist/state/database.d.ts.map +1 -0
- package/dist/state/database.js +679 -0
- package/dist/state/database.js.map +1 -0
- package/dist/state/index.d.ts +2 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +2 -0
- package/dist/state/index.js.map +1 -0
- package/dist/storage/fileSystem.d.ts +56 -0
- package/dist/storage/fileSystem.d.ts.map +1 -0
- package/dist/storage/fileSystem.js +129 -0
- package/dist/storage/fileSystem.js.map +1 -0
- package/package.json +71 -11
- package/cli.js +0 -45
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/shared/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAKhE,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,WAAW,UAAU;IACzB,oCAAoC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACtC,qEAAqE;IACrE,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACjD,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAcD;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,GAAE,MAAM,EAA2B,GAC1C,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAE1B;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,QAPpB,MAAM,KAAK,OAOoE,CAAC;AAEzF;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAZxB,MAAM,KAAK,OAmBlB,CAAC;AAOH;;GAEG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGtE;AAyBD;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAoDtF;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GACzD,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC,CAwErD;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGnE;AAED;;;GAGG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { chromium } from "playwright";
|
|
2
|
+
import { getSessionPath, SESSIONS_DIR } from "../config/paths.js";
|
|
3
|
+
import { ensureDir, outputJson, pathExists, readJson, removeFile } from "./fs.js";
|
|
4
|
+
/**
|
|
5
|
+
* Default login page detection patterns.
|
|
6
|
+
*/
|
|
7
|
+
const DEFAULT_LOGIN_PATTERNS = [
|
|
8
|
+
/\/login/,
|
|
9
|
+
/\/signin/,
|
|
10
|
+
/\/auth/,
|
|
11
|
+
/accounts\.google\.com/,
|
|
12
|
+
/firebaseapp\.com/,
|
|
13
|
+
/sso\./,
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* Creates a login page checker from patterns.
|
|
17
|
+
*/
|
|
18
|
+
export function createLoginChecker(patterns = DEFAULT_LOGIN_PATTERNS) {
|
|
19
|
+
return (url) => patterns.some((p) => p.test(url));
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Skool-specific login page checker.
|
|
23
|
+
*/
|
|
24
|
+
export const isSkoolLoginPage = createLoginChecker([/\/login/, /accounts\.google\.com/]);
|
|
25
|
+
/**
|
|
26
|
+
* HighLevel-specific login page checker.
|
|
27
|
+
*/
|
|
28
|
+
export const isHighLevelLoginPage = createLoginChecker([
|
|
29
|
+
/sso\.clientclub\.net/,
|
|
30
|
+
/\/login/,
|
|
31
|
+
/\/signin/,
|
|
32
|
+
/\/auth/,
|
|
33
|
+
/accounts\.google\.com/,
|
|
34
|
+
/firebaseapp\.com/,
|
|
35
|
+
]);
|
|
36
|
+
// ============================================
|
|
37
|
+
// Browser automation - not unit testable
|
|
38
|
+
// ============================================
|
|
39
|
+
/* v8 ignore start */
|
|
40
|
+
/**
|
|
41
|
+
* Checks if a valid session exists for the given domain.
|
|
42
|
+
*/
|
|
43
|
+
export async function hasValidSession(domain) {
|
|
44
|
+
const sessionPath = getSessionPath(domain);
|
|
45
|
+
return pathExists(sessionPath);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Loads an existing session from disk.
|
|
49
|
+
*/
|
|
50
|
+
async function loadSession(browser, domain) {
|
|
51
|
+
const sessionPath = getSessionPath(domain);
|
|
52
|
+
// Playwright's storageState type is complex, we load it as-is from JSON
|
|
53
|
+
const storageState = await readJson(sessionPath);
|
|
54
|
+
if (!storageState) {
|
|
55
|
+
throw new Error("Session file not found or invalid");
|
|
56
|
+
}
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
|
|
58
|
+
return browser.newContext({ storageState: storageState });
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Saves the current session to disk.
|
|
62
|
+
*/
|
|
63
|
+
async function saveSession(context, domain) {
|
|
64
|
+
const sessionPath = getSessionPath(domain);
|
|
65
|
+
const storageState = await context.storageState();
|
|
66
|
+
await outputJson(sessionPath, storageState);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Performs interactive login by opening a browser window.
|
|
70
|
+
* The user logs in manually, and we capture the session.
|
|
71
|
+
*/
|
|
72
|
+
export async function performInteractiveLogin(config) {
|
|
73
|
+
await ensureDir(SESSIONS_DIR);
|
|
74
|
+
const browser = await chromium.launch({
|
|
75
|
+
headless: false, // Must be visible for user interaction
|
|
76
|
+
});
|
|
77
|
+
const context = await browser.newContext({
|
|
78
|
+
viewport: { width: 1280, height: 800 },
|
|
79
|
+
});
|
|
80
|
+
const page = await context.newPage();
|
|
81
|
+
await page.goto(config.loginUrl);
|
|
82
|
+
console.log("\n🔐 Browser opened. Please log in manually.");
|
|
83
|
+
console.log(" The window will close automatically after successful login.\n");
|
|
84
|
+
const timeout = config.loginTimeout ?? 300000; // 5 minutes default
|
|
85
|
+
const startTime = Date.now();
|
|
86
|
+
let loggedIn = false;
|
|
87
|
+
while (!loggedIn && Date.now() - startTime < timeout) {
|
|
88
|
+
await page.waitForTimeout(1000);
|
|
89
|
+
const currentUrl = page.url();
|
|
90
|
+
// Check if we're no longer on a login page
|
|
91
|
+
if (!config.isLoginPage(currentUrl)) {
|
|
92
|
+
// Optionally verify with custom function
|
|
93
|
+
if (config.verifySession) {
|
|
94
|
+
loggedIn = await config.verifySession(page);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
loggedIn = true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (!loggedIn) {
|
|
102
|
+
await browser.close();
|
|
103
|
+
throw new Error(`Login timed out after ${timeout / 1000} seconds`);
|
|
104
|
+
}
|
|
105
|
+
// Give the page a moment to fully load after login
|
|
106
|
+
await page.waitForLoadState("networkidle").catch(() => { });
|
|
107
|
+
await page.waitForTimeout(1000);
|
|
108
|
+
// Save the session
|
|
109
|
+
await saveSession(context, config.domain);
|
|
110
|
+
console.log("✅ Login successful! Session saved.\n");
|
|
111
|
+
return { context, page };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Gets an authenticated session, either from cache or via interactive login.
|
|
115
|
+
*/
|
|
116
|
+
export async function getAuthenticatedSession(config, options = {}) {
|
|
117
|
+
const useHeadless = options.headless !== false;
|
|
118
|
+
const browser = await chromium.launch({
|
|
119
|
+
headless: useHeadless,
|
|
120
|
+
});
|
|
121
|
+
// Try to use existing session
|
|
122
|
+
if (!options.forceLogin && (await hasValidSession(config.domain))) {
|
|
123
|
+
try {
|
|
124
|
+
const context = await loadSession(browser, config.domain);
|
|
125
|
+
const page = await context.newPage();
|
|
126
|
+
// Navigate to verify session
|
|
127
|
+
await page.goto(config.loginUrl);
|
|
128
|
+
await page.waitForLoadState("domcontentloaded");
|
|
129
|
+
await page.waitForTimeout(2000);
|
|
130
|
+
const currentUrl = page.url();
|
|
131
|
+
// Check if we got redirected to login
|
|
132
|
+
if (config.isLoginPage(currentUrl)) {
|
|
133
|
+
console.log("⚠️ Session expired, need to re-login...");
|
|
134
|
+
await context.close();
|
|
135
|
+
await browser.close();
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
// Optionally verify session
|
|
139
|
+
if (config.verifySession) {
|
|
140
|
+
const isValid = await config.verifySession(page);
|
|
141
|
+
if (!isValid) {
|
|
142
|
+
console.log("⚠️ Session invalid, need to re-login...");
|
|
143
|
+
await context.close();
|
|
144
|
+
await browser.close();
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
console.log("✅ Using cached session");
|
|
148
|
+
return { browser, session: { context, page } };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
console.log("✅ Using cached session");
|
|
153
|
+
return { browser, session: { context, page } };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
console.log("⚠️ Failed to load session, need to re-login...", error);
|
|
159
|
+
await browser.close();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
await browser.close();
|
|
164
|
+
}
|
|
165
|
+
// Need fresh login - always visible for interactive login
|
|
166
|
+
const session = await performInteractiveLogin(config);
|
|
167
|
+
// Get the browser from the session context
|
|
168
|
+
const sessionBrowser = session.context.browser();
|
|
169
|
+
if (!sessionBrowser) {
|
|
170
|
+
throw new Error("Failed to get browser from session");
|
|
171
|
+
}
|
|
172
|
+
// After login, reopen with headless browser if needed
|
|
173
|
+
if (useHeadless) {
|
|
174
|
+
const newBrowser = await chromium.launch({ headless: true });
|
|
175
|
+
const context = await loadSession(newBrowser, config.domain);
|
|
176
|
+
const page = await context.newPage();
|
|
177
|
+
// Close the interactive session
|
|
178
|
+
await sessionBrowser.close();
|
|
179
|
+
return { browser: newBrowser, session: { context, page } };
|
|
180
|
+
}
|
|
181
|
+
return { browser: sessionBrowser, session };
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Clears the session for a domain.
|
|
185
|
+
*/
|
|
186
|
+
export async function clearSession(domain) {
|
|
187
|
+
const sessionPath = getSessionPath(domain);
|
|
188
|
+
return removeFile(sessionPath);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Checks if the page has a valid Firebase auth token.
|
|
192
|
+
* Used by HighLevel/GoHighLevel portals.
|
|
193
|
+
*/
|
|
194
|
+
// Re-export Firebase auth utilities (used by multiple platforms)
|
|
195
|
+
export { hasValidFirebaseToken } from "./firebase.js";
|
|
196
|
+
/* v8 ignore stop */
|
|
197
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/shared/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase Authentication utilities.
|
|
3
|
+
*
|
|
4
|
+
* Firebase Auth is used by many platforms (HighLevel, etc.) for user authentication.
|
|
5
|
+
* This module provides shared types, schemas, and utilities for Firebase Auth tokens
|
|
6
|
+
* stored in localStorage.
|
|
7
|
+
*/
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import type { Page } from "playwright";
|
|
10
|
+
/**
|
|
11
|
+
* Zod schema for validated Firebase auth token from localStorage.
|
|
12
|
+
* Use this after parsing to ensure the token has all required fields.
|
|
13
|
+
*/
|
|
14
|
+
export declare const FirebaseAuthTokenSchema: z.ZodObject<{
|
|
15
|
+
stsTokenManager: z.ZodObject<{
|
|
16
|
+
accessToken: z.ZodString;
|
|
17
|
+
expirationTime: z.ZodOptional<z.ZodNumber>;
|
|
18
|
+
refreshToken: z.ZodOptional<z.ZodString>;
|
|
19
|
+
}, z.core.$strip>;
|
|
20
|
+
}, z.core.$strip>;
|
|
21
|
+
export type FirebaseAuthToken = z.infer<typeof FirebaseAuthTokenSchema>;
|
|
22
|
+
/**
|
|
23
|
+
* Raw Firebase auth data structure from localStorage (before validation).
|
|
24
|
+
* Use this type when parsing JSON from localStorage.
|
|
25
|
+
*/
|
|
26
|
+
export interface FirebaseAuthRaw {
|
|
27
|
+
stsTokenManager?: {
|
|
28
|
+
accessToken?: string;
|
|
29
|
+
expirationTime?: number;
|
|
30
|
+
refreshToken?: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The localStorage key pattern for Firebase auth tokens.
|
|
35
|
+
* Firebase stores auth tokens with keys like "firebase:authUser:API_KEY:[DEFAULT]"
|
|
36
|
+
*/
|
|
37
|
+
export declare const FIREBASE_AUTH_KEY_PATTERN = "firebase:authUser";
|
|
38
|
+
/**
|
|
39
|
+
* Finds the Firebase auth token key in localStorage.
|
|
40
|
+
* Returns null if no Firebase auth token is found.
|
|
41
|
+
*/
|
|
42
|
+
export declare function findFirebaseAuthKey(storage: Storage): string | null;
|
|
43
|
+
/**
|
|
44
|
+
* Checks if a Firebase auth token is expired.
|
|
45
|
+
*/
|
|
46
|
+
export declare function isTokenExpired(token: FirebaseAuthRaw): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Extracts the access token from Firebase auth data.
|
|
49
|
+
*/
|
|
50
|
+
export declare function getAccessToken(token: FirebaseAuthRaw): string | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Checks if the page has a valid (non-expired) Firebase auth token in localStorage.
|
|
53
|
+
*/
|
|
54
|
+
export declare function hasValidFirebaseToken(page: Page): Promise<boolean>;
|
|
55
|
+
/**
|
|
56
|
+
* Extracts the Firebase auth token from the page's localStorage.
|
|
57
|
+
* Returns the raw token data or null if not found.
|
|
58
|
+
*/
|
|
59
|
+
export declare function extractFirebaseAuthFromPage(page: Page): Promise<FirebaseAuthRaw | null>;
|
|
60
|
+
//# sourceMappingURL=firebase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"firebase.d.ts","sourceRoot":"","sources":["../../src/shared/firebase.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAMvC;;;GAGG;AACH,eAAO,MAAM,uBAAuB;;;;;;iBAMlC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,eAAe,CAAC,EAAE;QAChB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAMD;;;GAGG;AACH,eAAO,MAAM,yBAAyB,sBAAsB,CAAC;AAE7D;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAEnE;AAMD;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAQ9D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,GAAG,SAAS,CAEzE;AAQD;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CA4BxE;AAED;;;GAGG;AACH,wBAAsB,2BAA2B,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAsB7F"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase Authentication utilities.
|
|
3
|
+
*
|
|
4
|
+
* Firebase Auth is used by many platforms (HighLevel, etc.) for user authentication.
|
|
5
|
+
* This module provides shared types, schemas, and utilities for Firebase Auth tokens
|
|
6
|
+
* stored in localStorage.
|
|
7
|
+
*/
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Schemas & Types
|
|
11
|
+
// ============================================================================
|
|
12
|
+
/**
|
|
13
|
+
* Zod schema for validated Firebase auth token from localStorage.
|
|
14
|
+
* Use this after parsing to ensure the token has all required fields.
|
|
15
|
+
*/
|
|
16
|
+
export const FirebaseAuthTokenSchema = z.object({
|
|
17
|
+
stsTokenManager: z.object({
|
|
18
|
+
accessToken: z.string(),
|
|
19
|
+
expirationTime: z.number().optional(),
|
|
20
|
+
refreshToken: z.string().optional(),
|
|
21
|
+
}),
|
|
22
|
+
});
|
|
23
|
+
// ============================================================================
|
|
24
|
+
// localStorage Key Detection
|
|
25
|
+
// ============================================================================
|
|
26
|
+
/**
|
|
27
|
+
* The localStorage key pattern for Firebase auth tokens.
|
|
28
|
+
* Firebase stores auth tokens with keys like "firebase:authUser:API_KEY:[DEFAULT]"
|
|
29
|
+
*/
|
|
30
|
+
export const FIREBASE_AUTH_KEY_PATTERN = "firebase:authUser";
|
|
31
|
+
/**
|
|
32
|
+
* Finds the Firebase auth token key in localStorage.
|
|
33
|
+
* Returns null if no Firebase auth token is found.
|
|
34
|
+
*/
|
|
35
|
+
export function findFirebaseAuthKey(storage) {
|
|
36
|
+
return Object.keys(storage).find((k) => k.includes(FIREBASE_AUTH_KEY_PATTERN)) ?? null;
|
|
37
|
+
}
|
|
38
|
+
// ============================================================================
|
|
39
|
+
// Token Utilities
|
|
40
|
+
// ============================================================================
|
|
41
|
+
/**
|
|
42
|
+
* Checks if a Firebase auth token is expired.
|
|
43
|
+
*/
|
|
44
|
+
export function isTokenExpired(token) {
|
|
45
|
+
const expirationTime = token.stsTokenManager?.expirationTime;
|
|
46
|
+
if (!expirationTime) {
|
|
47
|
+
// No expiration time means we can't determine if expired
|
|
48
|
+
// Assume valid if we have an access token
|
|
49
|
+
return !token.stsTokenManager?.accessToken;
|
|
50
|
+
}
|
|
51
|
+
return Date.now() >= expirationTime;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Extracts the access token from Firebase auth data.
|
|
55
|
+
*/
|
|
56
|
+
export function getAccessToken(token) {
|
|
57
|
+
return token.stsTokenManager?.accessToken;
|
|
58
|
+
}
|
|
59
|
+
// ============================================================================
|
|
60
|
+
// Page Utilities (Playwright)
|
|
61
|
+
// ============================================================================
|
|
62
|
+
/* v8 ignore start */
|
|
63
|
+
/**
|
|
64
|
+
* Checks if the page has a valid (non-expired) Firebase auth token in localStorage.
|
|
65
|
+
*/
|
|
66
|
+
export async function hasValidFirebaseToken(page) {
|
|
67
|
+
try {
|
|
68
|
+
return await page.evaluate(({ keyPattern }) => {
|
|
69
|
+
const tokenKey = Object.keys(localStorage).find((k) => k.includes(keyPattern));
|
|
70
|
+
if (!tokenKey)
|
|
71
|
+
return false;
|
|
72
|
+
const tokenData = JSON.parse(localStorage.getItem(tokenKey) ?? "{}");
|
|
73
|
+
const expirationTime = tokenData?.stsTokenManager?.expirationTime;
|
|
74
|
+
if (expirationTime) {
|
|
75
|
+
return Date.now() < expirationTime;
|
|
76
|
+
}
|
|
77
|
+
return !!tokenData?.stsTokenManager?.accessToken;
|
|
78
|
+
}, { keyPattern: FIREBASE_AUTH_KEY_PATTERN });
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Extracts the Firebase auth token from the page's localStorage.
|
|
86
|
+
* Returns the raw token data or null if not found.
|
|
87
|
+
*/
|
|
88
|
+
export async function extractFirebaseAuthFromPage(page) {
|
|
89
|
+
return page.evaluate(({ keyPattern }) => {
|
|
90
|
+
const tokenKey = Object.keys(localStorage).find((k) => k.includes(keyPattern));
|
|
91
|
+
if (!tokenKey)
|
|
92
|
+
return null;
|
|
93
|
+
try {
|
|
94
|
+
return JSON.parse(localStorage.getItem(tokenKey) ?? "{}");
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}, { keyPattern: FIREBASE_AUTH_KEY_PATTERN });
|
|
100
|
+
}
|
|
101
|
+
/* v8 ignore stop */
|
|
102
|
+
//# sourceMappingURL=firebase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"firebase.js","sourceRoot":"","sources":["../../src/shared/firebase.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if a file or directory exists.
|
|
3
|
+
*/
|
|
4
|
+
export declare function pathExists(path: string): Promise<boolean>;
|
|
5
|
+
/**
|
|
6
|
+
* Ensure a directory exists, creating it recursively if needed.
|
|
7
|
+
*/
|
|
8
|
+
export declare function ensureDir(dir: string): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Write a file, creating parent directories if needed.
|
|
11
|
+
*/
|
|
12
|
+
export declare function outputFile(path: string, data: string): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Write JSON to a file, creating parent directories if needed.
|
|
15
|
+
*/
|
|
16
|
+
export declare function outputJson(path: string, data: unknown): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Read and parse a JSON file.
|
|
19
|
+
* Returns null if file doesn't exist or can't be parsed.
|
|
20
|
+
*/
|
|
21
|
+
export declare function readJson<T = unknown>(path: string): Promise<T | null>;
|
|
22
|
+
/**
|
|
23
|
+
* Remove a file if it exists.
|
|
24
|
+
*/
|
|
25
|
+
export declare function removeFile(path: string): Promise<boolean>;
|
|
26
|
+
/**
|
|
27
|
+
* Get file size in bytes, or null if file doesn't exist.
|
|
28
|
+
*/
|
|
29
|
+
export declare function getFileSize(path: string): Promise<number | null>;
|
|
30
|
+
export { readFile, writeFile, mkdir, unlink, stat } from "node:fs/promises";
|
|
31
|
+
//# sourceMappingURL=fs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/shared/fs.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1D;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG1E;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3E;AAED;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAO3E;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOtE;AAGD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin wrappers around fs/promises for common operations.
|
|
3
|
+
* Excluded from coverage - testing would just test Node.js itself.
|
|
4
|
+
*/
|
|
5
|
+
import { mkdir, readFile, writeFile, unlink, access, stat } from "node:fs/promises";
|
|
6
|
+
import { dirname } from "node:path";
|
|
7
|
+
/**
|
|
8
|
+
* Check if a file or directory exists.
|
|
9
|
+
*/
|
|
10
|
+
export async function pathExists(path) {
|
|
11
|
+
try {
|
|
12
|
+
await access(path);
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Ensure a directory exists, creating it recursively if needed.
|
|
21
|
+
*/
|
|
22
|
+
export async function ensureDir(dir) {
|
|
23
|
+
await mkdir(dir, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Write a file, creating parent directories if needed.
|
|
27
|
+
*/
|
|
28
|
+
export async function outputFile(path, data) {
|
|
29
|
+
await ensureDir(dirname(path));
|
|
30
|
+
await writeFile(path, data, "utf-8");
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Write JSON to a file, creating parent directories if needed.
|
|
34
|
+
*/
|
|
35
|
+
export async function outputJson(path, data) {
|
|
36
|
+
await outputFile(path, JSON.stringify(data, null, 2));
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Read and parse a JSON file.
|
|
40
|
+
* Returns null if file doesn't exist or can't be parsed.
|
|
41
|
+
*/
|
|
42
|
+
export async function readJson(path) {
|
|
43
|
+
try {
|
|
44
|
+
const content = await readFile(path, "utf-8");
|
|
45
|
+
return JSON.parse(content);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Remove a file if it exists.
|
|
53
|
+
*/
|
|
54
|
+
export async function removeFile(path) {
|
|
55
|
+
try {
|
|
56
|
+
await unlink(path);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get file size in bytes, or null if file doesn't exist.
|
|
65
|
+
*/
|
|
66
|
+
export async function getFileSize(path) {
|
|
67
|
+
try {
|
|
68
|
+
const stats = await stat(path);
|
|
69
|
+
return stats.size;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Re-export commonly used fs/promises functions
|
|
76
|
+
export { readFile, writeFile, mkdir, unlink, stat } from "node:fs/promises";
|
|
77
|
+
//# sourceMappingURL=fs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/shared/fs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACpF,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY;IAC3C,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW;IACzC,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AACxC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY,EAAE,IAAY;IACzD,MAAM,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/B,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY,EAAE,IAAa;IAC1D,MAAM,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAc,IAAY;IACtD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAM,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY;IAC3C,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY;IAC5C,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,OAAO,KAAK,CAAC,IAAI,CAAC;IACpB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,gDAAgD;AAChD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default User-Agent for HTTP requests.
|
|
3
|
+
* Mimics a standard Chrome browser on macOS.
|
|
4
|
+
*/
|
|
5
|
+
export declare const USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
|
|
6
|
+
/**
|
|
7
|
+
* Pre-configured HTTP client with sensible defaults.
|
|
8
|
+
* Uses ky for automatic retries, better error handling, and cleaner API.
|
|
9
|
+
*/
|
|
10
|
+
export declare const http: import("ky").KyInstance;
|
|
11
|
+
/**
|
|
12
|
+
* HTTP client configured for JSON APIs.
|
|
13
|
+
*/
|
|
14
|
+
export declare const httpJson: import("ky").KyInstance;
|
|
15
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/shared/http.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,UAAU,0HACkG,CAAC;AAE1H;;;GAGG;AACH,eAAO,MAAM,IAAI,yBAWf,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,QAAQ,yBAInB,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import ky from "ky";
|
|
2
|
+
/**
|
|
3
|
+
* Default User-Agent for HTTP requests.
|
|
4
|
+
* Mimics a standard Chrome browser on macOS.
|
|
5
|
+
*/
|
|
6
|
+
export const USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
|
|
7
|
+
/**
|
|
8
|
+
* Pre-configured HTTP client with sensible defaults.
|
|
9
|
+
* Uses ky for automatic retries, better error handling, and cleaner API.
|
|
10
|
+
*/
|
|
11
|
+
export const http = ky.create({
|
|
12
|
+
headers: {
|
|
13
|
+
"User-Agent": USER_AGENT,
|
|
14
|
+
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
|
15
|
+
"Accept-Language": "en-US,en;q=0.5",
|
|
16
|
+
},
|
|
17
|
+
timeout: 30000,
|
|
18
|
+
retry: {
|
|
19
|
+
limit: 2,
|
|
20
|
+
statusCodes: [408, 413, 429, 500, 502, 503, 504],
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* HTTP client configured for JSON APIs.
|
|
25
|
+
*/
|
|
26
|
+
export const httpJson = http.extend({
|
|
27
|
+
headers: {
|
|
28
|
+
Accept: "application/json",
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/shared/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GACrB,uHAAuH,CAAC;AAE1H;;;GAGG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC,MAAM,CAAC;IAC5B,OAAO,EAAE;QACP,YAAY,EAAE,UAAU;QACxB,MAAM,EAAE,4EAA4E;QACpF,iBAAiB,EAAE,gBAAgB;KACpC;IACD,OAAO,EAAE,KAAK;IACd,KAAK,EAAE;QACL,KAAK,EAAE,CAAC;QACR,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;KACjD;CACF,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC;IAClC,OAAO,EAAE;QACP,MAAM,EAAE,kBAAkB;KAC3B;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shared/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/shared/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a filesystem-safe slug from a string.
|
|
3
|
+
* Handles Unicode characters, special symbols, and edge cases.
|
|
4
|
+
*/
|
|
5
|
+
export declare function slugify(name: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Creates a folder name with zero-padded index prefix.
|
|
8
|
+
* Example: createFolderName(0, "Introduction") → "01-introduction"
|
|
9
|
+
*/
|
|
10
|
+
export declare function createFolderName(index: number, name: string): string;
|
|
11
|
+
//# sourceMappingURL=slug.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slug.d.ts","sourceRoot":"","sources":["../../src/shared/slug.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK5C;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAIpE"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared slugification utilities.
|
|
3
|
+
* Uses @sindresorhus/slugify for proper Unicode transliteration.
|
|
4
|
+
*/
|
|
5
|
+
import slugifyLib from "@sindresorhus/slugify";
|
|
6
|
+
/**
|
|
7
|
+
* Creates a filesystem-safe slug from a string.
|
|
8
|
+
* Handles Unicode characters, special symbols, and edge cases.
|
|
9
|
+
*/
|
|
10
|
+
export function slugify(name) {
|
|
11
|
+
return slugifyLib(name, {
|
|
12
|
+
lowercase: true,
|
|
13
|
+
separator: "-",
|
|
14
|
+
}).substring(0, 100);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Creates a folder name with zero-padded index prefix.
|
|
18
|
+
* Example: createFolderName(0, "Introduction") → "01-introduction"
|
|
19
|
+
*/
|
|
20
|
+
export function createFolderName(index, name) {
|
|
21
|
+
const prefix = String(index + 1).padStart(2, "0");
|
|
22
|
+
const slug = slugify(name);
|
|
23
|
+
return `${prefix}-${slug}`;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=slug.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slug.js","sourceRoot":"","sources":["../../src/shared/slug.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,UAAU,MAAM,uBAAuB,CAAC;AAE/C;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,UAAU,CAAC,IAAI,EAAE;QACtB,SAAS,EAAE,IAAI;QACf,SAAS,EAAE,GAAG;KACf,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,IAAY;IAC1D,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC;AAC7B,CAAC"}
|