Initial commit: MangaReader iOS App
✨ Features: - App iOS completa para leer manga sin publicidad - Scraper con WKWebView para manhwaweb.com - Sistema de descargas offline - Lector con zoom y navegación - Favoritos y progreso de lectura - Compatible con iOS 15+ y Sideloadly/3uTools 📦 Contenido: - Backend Node.js con Puppeteer (opcional) - App iOS con SwiftUI - Scraper de capítulos e imágenes - Sistema de almacenamiento local - Testing completo - Documentación exhaustiva 🧪 Prueba: Capítulo 789 de One Piece descargado exitosamente - 21 páginas descargadas - 4.68 MB total - URLs verificadas y funcionales 🎉 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1145
backend/node_modules/cheerio/src/api/attributes.ts
generated
vendored
Normal file
1145
backend/node_modules/cheerio/src/api/attributes.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
224
backend/node_modules/cheerio/src/api/css.ts
generated
vendored
Normal file
224
backend/node_modules/cheerio/src/api/css.ts
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
import { domEach } from '../utils.js';
|
||||
import { isTag, type Element, type AnyNode } from 'domhandler';
|
||||
import type { Cheerio } from '../cheerio.js';
|
||||
|
||||
/**
|
||||
* Get the value of a style property for the first element in the set of matched
|
||||
* elements.
|
||||
*
|
||||
* @category CSS
|
||||
* @param names - Optionally the names of the properties of interest.
|
||||
* @returns A map of all of the style properties.
|
||||
* @see {@link https://api.jquery.com/css/}
|
||||
*/
|
||||
export function css<T extends AnyNode>(
|
||||
this: Cheerio<T>,
|
||||
names?: string[],
|
||||
): Record<string, string> | undefined;
|
||||
/**
|
||||
* Get the value of a style property for the first element in the set of matched
|
||||
* elements.
|
||||
*
|
||||
* @category CSS
|
||||
* @param name - The name of the property.
|
||||
* @returns The property value for the given name.
|
||||
* @see {@link https://api.jquery.com/css/}
|
||||
*/
|
||||
export function css<T extends AnyNode>(
|
||||
this: Cheerio<T>,
|
||||
name: string,
|
||||
): string | undefined;
|
||||
/**
|
||||
* Set one CSS property for every matched element.
|
||||
*
|
||||
* @category CSS
|
||||
* @param prop - The name of the property.
|
||||
* @param val - The new value.
|
||||
* @returns The instance itself.
|
||||
* @see {@link https://api.jquery.com/css/}
|
||||
*/
|
||||
export function css<T extends AnyNode>(
|
||||
this: Cheerio<T>,
|
||||
prop: string,
|
||||
val:
|
||||
| string
|
||||
| ((this: Element, i: number, style: string) => string | undefined),
|
||||
): Cheerio<T>;
|
||||
/**
|
||||
* Set multiple CSS properties for every matched element.
|
||||
*
|
||||
* @category CSS
|
||||
* @param map - A map of property names and values.
|
||||
* @returns The instance itself.
|
||||
* @see {@link https://api.jquery.com/css/}
|
||||
*/
|
||||
export function css<T extends AnyNode>(
|
||||
this: Cheerio<T>,
|
||||
map: Record<string, string>,
|
||||
): Cheerio<T>;
|
||||
/**
|
||||
* Set multiple CSS properties for every matched element.
|
||||
*
|
||||
* @category CSS
|
||||
* @param prop - The names of the properties.
|
||||
* @param val - The new values.
|
||||
* @returns The instance itself.
|
||||
* @see {@link https://api.jquery.com/css/}
|
||||
*/
|
||||
export function css<T extends AnyNode>(
|
||||
this: Cheerio<T>,
|
||||
prop?: string | string[] | Record<string, string>,
|
||||
val?:
|
||||
| string
|
||||
| ((this: Element, i: number, style: string) => string | undefined),
|
||||
): Cheerio<T> | Record<string, string> | string | undefined {
|
||||
if (
|
||||
(prop != null && val != null) ||
|
||||
// When `prop` is a "plain" object
|
||||
(typeof prop === 'object' && !Array.isArray(prop))
|
||||
) {
|
||||
return domEach(this, (el, i) => {
|
||||
if (isTag(el)) {
|
||||
// `prop` can't be an array here anymore.
|
||||
setCss(el, prop as string, val, i);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return getCss(this[0], prop as string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set styles of all elements.
|
||||
*
|
||||
* @private
|
||||
* @param el - Element to set style of.
|
||||
* @param prop - Name of property.
|
||||
* @param value - Value to set property to.
|
||||
* @param idx - Optional index within the selection.
|
||||
*/
|
||||
function setCss(
|
||||
el: Element,
|
||||
prop: string | Record<string, string>,
|
||||
value:
|
||||
| string
|
||||
| ((this: Element, i: number, style: string) => string | undefined)
|
||||
| undefined,
|
||||
idx: number,
|
||||
) {
|
||||
if (typeof prop === 'string') {
|
||||
const styles = getCss(el);
|
||||
|
||||
const val =
|
||||
typeof value === 'function' ? value.call(el, idx, styles[prop]) : value;
|
||||
|
||||
if (val === '') {
|
||||
delete styles[prop];
|
||||
} else if (val != null) {
|
||||
styles[prop] = val;
|
||||
}
|
||||
|
||||
el.attribs['style'] = stringify(styles);
|
||||
} else if (typeof prop === 'object') {
|
||||
const keys = Object.keys(prop);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const k = keys[i];
|
||||
setCss(el, k, prop[k], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parsed styles of the first element.
|
||||
*
|
||||
* @private
|
||||
* @category CSS
|
||||
* @param el - Element to get styles from.
|
||||
* @param props - Optionally the names of the properties of interest.
|
||||
* @returns The parsed styles.
|
||||
*/
|
||||
function getCss(el: AnyNode, props?: string[]): Record<string, string>;
|
||||
/**
|
||||
* Get a property from the parsed styles of the first element.
|
||||
*
|
||||
* @private
|
||||
* @category CSS
|
||||
* @param el - Element to get styles from.
|
||||
* @param prop - Name of the prop.
|
||||
* @returns The value of the property.
|
||||
*/
|
||||
function getCss(el: AnyNode, prop: string): string | undefined;
|
||||
function getCss(
|
||||
el: AnyNode,
|
||||
prop?: string | string[],
|
||||
): Record<string, string> | string | undefined {
|
||||
if (!el || !isTag(el)) return;
|
||||
|
||||
const styles = parse(el.attribs['style']);
|
||||
if (typeof prop === 'string') {
|
||||
return styles[prop];
|
||||
}
|
||||
if (Array.isArray(prop)) {
|
||||
const newStyles: Record<string, string> = {};
|
||||
for (const item of prop) {
|
||||
if (styles[item] != null) {
|
||||
newStyles[item] = styles[item];
|
||||
}
|
||||
}
|
||||
return newStyles;
|
||||
}
|
||||
return styles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stringify `obj` to styles.
|
||||
*
|
||||
* @private
|
||||
* @category CSS
|
||||
* @param obj - Object to stringify.
|
||||
* @returns The serialized styles.
|
||||
*/
|
||||
function stringify(obj: Record<string, string>): string {
|
||||
return Object.keys(obj).reduce(
|
||||
(str, prop) => `${str}${str ? ' ' : ''}${prop}: ${obj[prop]};`,
|
||||
'',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse `styles`.
|
||||
*
|
||||
* @private
|
||||
* @category CSS
|
||||
* @param styles - Styles to be parsed.
|
||||
* @returns The parsed styles.
|
||||
*/
|
||||
function parse(styles: string): Record<string, string> {
|
||||
styles = (styles || '').trim();
|
||||
|
||||
if (!styles) return {};
|
||||
|
||||
const obj: Record<string, string> = {};
|
||||
|
||||
let key: string | undefined;
|
||||
|
||||
for (const str of styles.split(';')) {
|
||||
const n = str.indexOf(':');
|
||||
// If there is no :, or if it is the first/last character, add to the previous item's value
|
||||
if (n < 1 || n === str.length - 1) {
|
||||
const trimmed = str.trimEnd();
|
||||
if (trimmed.length > 0 && key !== undefined) {
|
||||
obj[key] += `;${trimmed}`;
|
||||
}
|
||||
} else {
|
||||
key = str.slice(0, n).trim();
|
||||
obj[key] = str.slice(n + 1).trim();
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
92
backend/node_modules/cheerio/src/api/extract.ts
generated
vendored
Normal file
92
backend/node_modules/cheerio/src/api/extract.ts
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
import type { AnyNode, Element } from 'domhandler';
|
||||
import type { Cheerio } from '../cheerio.js';
|
||||
import type { prop } from './attributes.js';
|
||||
|
||||
type ExtractDescriptorFn = (
|
||||
el: Element,
|
||||
key: string,
|
||||
// TODO: This could be typed with ExtractedMap
|
||||
obj: Record<string, unknown>,
|
||||
) => unknown;
|
||||
|
||||
interface ExtractDescriptor {
|
||||
selector: string;
|
||||
value?: string | ExtractDescriptorFn | ExtractMap;
|
||||
}
|
||||
|
||||
type ExtractValue = string | ExtractDescriptor | [string | ExtractDescriptor];
|
||||
|
||||
export type ExtractMap = Record<string, ExtractValue>;
|
||||
|
||||
type ExtractedValue<V extends ExtractValue> = V extends [
|
||||
string | ExtractDescriptor,
|
||||
]
|
||||
? NonNullable<ExtractedValue<V[0]>>[]
|
||||
: V extends string
|
||||
? string | undefined
|
||||
: V extends ExtractDescriptor
|
||||
? V['value'] extends infer U
|
||||
? U extends ExtractMap
|
||||
? ExtractedMap<U> | undefined
|
||||
: U extends ExtractDescriptorFn
|
||||
? ReturnType<U> | undefined
|
||||
: ReturnType<typeof prop> | undefined
|
||||
: never
|
||||
: never;
|
||||
|
||||
export type ExtractedMap<M extends ExtractMap> = {
|
||||
[key in keyof M]: ExtractedValue<M[key]>;
|
||||
};
|
||||
|
||||
function getExtractDescr(
|
||||
descr: string | ExtractDescriptor,
|
||||
): Required<ExtractDescriptor> {
|
||||
if (typeof descr === 'string') {
|
||||
return { selector: descr, value: 'textContent' };
|
||||
}
|
||||
|
||||
return {
|
||||
selector: descr.selector,
|
||||
value: descr.value ?? 'textContent',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract multiple values from a document, and store them in an object.
|
||||
*
|
||||
* @param map - An object containing key-value pairs. The keys are the names of
|
||||
* the properties to be created on the object, and the values are the
|
||||
* selectors to be used to extract the values.
|
||||
* @returns An object containing the extracted values.
|
||||
*/
|
||||
export function extract<M extends ExtractMap, T extends AnyNode>(
|
||||
this: Cheerio<T>,
|
||||
map: M,
|
||||
): ExtractedMap<M> {
|
||||
const ret: Record<string, unknown> = {};
|
||||
|
||||
for (const key in map) {
|
||||
const descr = map[key];
|
||||
const isArray = Array.isArray(descr);
|
||||
|
||||
const { selector, value } = getExtractDescr(isArray ? descr[0] : descr);
|
||||
|
||||
const fn: ExtractDescriptorFn =
|
||||
typeof value === 'function'
|
||||
? value
|
||||
: typeof value === 'string'
|
||||
? (el: Element) => this._make(el).prop(value)
|
||||
: (el: Element) => this._make(el).extract(value);
|
||||
|
||||
if (isArray) {
|
||||
ret[key] = this._findBySelector(selector, Number.POSITIVE_INFINITY)
|
||||
.map((_, el) => fn(el, key, ret))
|
||||
.get();
|
||||
} else {
|
||||
const $ = this._findBySelector(selector, 1);
|
||||
ret[key] = $.length > 0 ? fn($[0], key, ret) : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return ret as ExtractedMap<M>;
|
||||
}
|
||||
103
backend/node_modules/cheerio/src/api/forms.ts
generated
vendored
Normal file
103
backend/node_modules/cheerio/src/api/forms.ts
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
import { isTag, type AnyNode } from 'domhandler';
|
||||
import type { Cheerio } from '../cheerio.js';
|
||||
|
||||
/*
|
||||
* https://github.com/jquery/jquery/blob/2.1.3/src/manipulation/var/rcheckableType.js
|
||||
* https://github.com/jquery/jquery/blob/2.1.3/src/serialize.js
|
||||
*/
|
||||
const submittableSelector = 'input,select,textarea,keygen';
|
||||
const r20 = /%20/g;
|
||||
const rCRLF = /\r?\n/g;
|
||||
|
||||
/**
|
||||
* Encode a set of form elements as a string for submission.
|
||||
*
|
||||
* @category Forms
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
* $('<form><input name="foo" value="bar" /></form>').serialize();
|
||||
* //=> 'foo=bar'
|
||||
* ```
|
||||
*
|
||||
* @returns The serialized form.
|
||||
* @see {@link https://api.jquery.com/serialize/}
|
||||
*/
|
||||
export function serialize<T extends AnyNode>(this: Cheerio<T>): string {
|
||||
// Convert form elements into name/value objects
|
||||
const arr = this.serializeArray();
|
||||
|
||||
// Serialize each element into a key/value string
|
||||
const retArr = arr.map(
|
||||
(data) =>
|
||||
`${encodeURIComponent(data.name)}=${encodeURIComponent(data.value)}`,
|
||||
);
|
||||
|
||||
// Return the resulting serialization
|
||||
return retArr.join('&').replace(r20, '+');
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a set of form elements as an array of names and values.
|
||||
*
|
||||
* @category Forms
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
* $('<form><input name="foo" value="bar" /></form>').serializeArray();
|
||||
* //=> [ { name: 'foo', value: 'bar' } ]
|
||||
* ```
|
||||
*
|
||||
* @returns The serialized form.
|
||||
* @see {@link https://api.jquery.com/serializeArray/}
|
||||
*/
|
||||
export function serializeArray<T extends AnyNode>(
|
||||
this: Cheerio<T>,
|
||||
): {
|
||||
name: string;
|
||||
value: string;
|
||||
}[] {
|
||||
// Resolve all form elements from either forms or collections of form elements
|
||||
return this.map((_, elem) => {
|
||||
const $elem = this._make(elem);
|
||||
if (isTag(elem) && elem.name === 'form') {
|
||||
return $elem.find(submittableSelector).toArray();
|
||||
}
|
||||
return $elem.filter(submittableSelector).toArray();
|
||||
})
|
||||
.filter(
|
||||
// Verify elements have a name (`attr.name`) and are not disabled (`:enabled`)
|
||||
'[name!=""]:enabled' +
|
||||
// And cannot be clicked (`[type=submit]`) or are used in `x-www-form-urlencoded` (`[type=file]`)
|
||||
':not(:submit, :button, :image, :reset, :file)' +
|
||||
// And are either checked/don't have a checkable state
|
||||
':matches([checked], :not(:checkbox, :radio))',
|
||||
// Convert each of the elements to its value(s)
|
||||
)
|
||||
.map<
|
||||
AnyNode,
|
||||
{
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
>((_, elem) => {
|
||||
const $elem = this._make(elem);
|
||||
const name = $elem.attr('name')!; // We have filtered for elements with a name before.
|
||||
// If there is no value set (e.g. `undefined`, `null`), then default value to empty
|
||||
const value = $elem.val() ?? '';
|
||||
|
||||
// If we have an array of values (e.g. `<select multiple>`), return an array of key/value pairs
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((val) =>
|
||||
/*
|
||||
* We trim replace any line endings (e.g. `\r` or `\r\n` with `\r\n`) to guarantee consistency across platforms
|
||||
* These can occur inside of `<textarea>'s`
|
||||
*/
|
||||
({ name, value: val.replace(rCRLF, '\r\n') }),
|
||||
);
|
||||
}
|
||||
// Otherwise (e.g. `<input type="text">`, return only one key/value pair
|
||||
return { name, value: value.replace(rCRLF, '\r\n') };
|
||||
})
|
||||
.toArray();
|
||||
}
|
||||
1115
backend/node_modules/cheerio/src/api/manipulation.ts
generated
vendored
Normal file
1115
backend/node_modules/cheerio/src/api/manipulation.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1175
backend/node_modules/cheerio/src/api/traversing.ts
generated
vendored
Normal file
1175
backend/node_modules/cheerio/src/api/traversing.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user