Comparing version 0.0.755 to 0.0.756
{ | ||
"name": "spica", | ||
"version": "0.0.755", | ||
"version": "0.0.756", | ||
"description": "Supervisor, Coroutine, Channel, select, AtomicPromise, Cancellation, Cache, List, Queue, Stack, and some utils.", | ||
@@ -5,0 +5,0 @@ "private": false, |
@@ -1,2 +0,2 @@ | ||
import { URL, StandardURL, standardize } from './url'; | ||
import { URL, ReadonlyURL, StandardURL, standardize } from './url'; | ||
@@ -15,2 +15,7 @@ describe('Unit: lib/url', () => { | ||
it('type', () => { | ||
(): global.URL => new URL('', ''); | ||
(): global.URL => new ReadonlyURL('', ''); | ||
}); | ||
it('relative', () => { | ||
@@ -17,0 +22,0 @@ // @ts-expect-error |
20
url.ts
@@ -16,7 +16,9 @@ import { AbsoluteURL, ReadonlyURL } from './url/format'; | ||
[T]); | ||
constructor( | ||
public readonly source: string, | ||
public readonly base?: string, | ||
) { | ||
constructor(source: string, base?: string) { | ||
source = source.trim(); | ||
base = base?.trim(); | ||
this.url = new ReadonlyURL(source, base!); | ||
this.params = undefined; | ||
this.source = source; | ||
this.base = base; | ||
assert(this.url.href.endsWith(`${this.port}${this.pathname}${this.query}${this.fragment}`)); | ||
@@ -32,3 +34,5 @@ assert(this.href === this.url.href); | ||
private readonly url: ReadonlyURL; | ||
private params: URLSearchParams | undefined; | ||
private params?: URLSearchParams; | ||
public readonly source: string; | ||
public readonly base?: string; | ||
public get href(): URL.Href<T> { | ||
@@ -46,3 +50,3 @@ return this.params?.toString().replace(/^(?=.)/, `${this.url.href.slice(0, -this.url.query.length - this.url.fragment.length || this.url.href.length)}?`).concat(this.fragment) | ||
public get scheme(): URL.Scheme { | ||
return this.url.protocol.slice(0, -1) as any; | ||
return this.url.scheme as any; | ||
} | ||
@@ -93,6 +97,6 @@ public get protocol(): URL.Protocol { | ||
public toString(): string { | ||
return this.href; | ||
return this.url.toString(); | ||
} | ||
public toJSON(): string { | ||
return this.href; | ||
return this.url.toJSON(); | ||
} | ||
@@ -99,0 +103,0 @@ } |
@@ -47,3 +47,3 @@ import { standardize, _encode as encode } from './format'; | ||
it('percent-encoding', () => { | ||
assert(standardize('?a=b +c&%%3f#/?= +&%%3f#', location.href).endsWith(`?a=b%20%2Bc&%25%3F#/?=%20+&%%3f#`)); | ||
assert(standardize('?a=b+c&%%3f#/?=+&%%3f#', location.href).endsWith(`?a=b%2Bc&%25%3F#/?=+&%%3f#`)); | ||
}); | ||
@@ -53,2 +53,3 @@ | ||
assert(standardize(standardize('/%%3f%3d', location.href) as string).endsWith('/%25%3F%3D')); | ||
assert(standardize(standardize('?a b#a b', location.href) as string).endsWith(`?a%20b#a%20b`)); | ||
}); | ||
@@ -55,0 +56,0 @@ |
@@ -32,6 +32,6 @@ import '../global'; | ||
export function standardize(url: string, base?: string): StandardURL { | ||
const u = new ReadonlyURL(url, base!); | ||
url = u.origin === 'null' | ||
? u.protocol.toLowerCase() + u.href.slice(u.protocol.length) | ||
: u.origin.toLowerCase() + u.href.slice(u.origin.length); | ||
const { origin, protocol, href } = new ReadonlyURL(url, base!); | ||
url = origin === 'null' | ||
? protocol.toLowerCase() + href.slice(protocol.length) | ||
: origin.toLowerCase() + href.slice(origin.length); | ||
return encode(url as AbsoluteURL); | ||
@@ -48,19 +48,12 @@ } | ||
assert(url === url.trim()); | ||
return url | ||
.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]?|[\uDC00-\uDFFF]/g, str => | ||
str.length === 2 | ||
? str | ||
: '') | ||
// Percent-encoding | ||
.replace(/%(?![0-9A-F]{2})|[^%\[\]\w]+/ig, encodeURI) | ||
.replace(/\?[^#]+/, query => | ||
'?' + | ||
query.slice(1) | ||
.replace(/%[0-9A-F]{2}|%|[^%=&]+/ig, str => | ||
str[0] === '%' && str.length === 3 | ||
? str | ||
: encodeURIComponent(str))) | ||
// Use uppercase letters within percent-encoding triplets | ||
.replace(/%[0-9A-F]{2}/ig, str => str.toUpperCase()) | ||
.replace(/#.+/, url.slice(url.indexOf('#'))) as EncodedURL; | ||
url = url.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, ''); | ||
const { 1: base, 2: hash } = url | ||
.match(/^([^#]*)(.*)$/s)!; | ||
const { 1: path, 2: query } = base | ||
.replace(/(?:%(?:[0-9][a-f]|[a-f][0-9a-fA-F]|[A-F][0-9a-f]))+/g, str => str.toUpperCase()) | ||
.match(/^([^?]*)(.*)$/s)!; | ||
return '' | ||
+ path.replace(/(?:[^%[\]]|%(?![0-9A-F]{2}))+/ig, encodeURI) | ||
+ query.replace(/(?!^)(?:[^%=&]|%(?![0-9A-F]{2}))+/ig, encodeURIComponent) | ||
+ hash as EncodedURL; | ||
} | ||
@@ -70,5 +63,7 @@ export { encode as _encode } | ||
type SharedURL = Partial<Mutable<global.URL>> & { | ||
type CachedURL<T extends string> = Partial<Mutable<global.URL>> & { | ||
url: global.URL; | ||
href?: T; | ||
resource?: string; | ||
scheme?: string; | ||
path?: string; | ||
@@ -83,3 +78,3 @@ query?: string; | ||
// @ts-ignore | ||
private static readonly get = memoize((url: string, base: string | undefined): SharedURL => | ||
private static readonly get = memoize((url: string, base: string | undefined): CachedURL => | ||
({ url: new global.URL(url, base) }), | ||
@@ -92,6 +87,5 @@ (url, base = '') => `${base.indexOf('\n') > -1 ? base.replace(/\n+/g, '') : base}\n${url}`, | ||
[T]); | ||
constructor( | ||
public readonly source: string, | ||
public readonly base?: string, | ||
) { | ||
constructor(source: string, base?: string) { | ||
source = source.trim(); | ||
base = base?.trim(); | ||
switch (source.slice(0, source.lastIndexOf('://', 9) + 1).toLowerCase()) { | ||
@@ -111,3 +105,3 @@ case 'http:': | ||
const j = base.indexOf('?'); | ||
if (i > -1 && source.indexOf('#') === -1) { | ||
if (j > -1 && source !== '' && source[0] !== '#') { | ||
base = base.slice(0, j); | ||
@@ -117,69 +111,78 @@ } | ||
} | ||
this.share = ReadonlyURL.get(source, base); | ||
this.cache = ReadonlyURL.get(source, base); | ||
this.params = undefined; | ||
this.source = source; | ||
this.base = base; | ||
} | ||
private readonly share: SharedURL; | ||
private params: URLSearchParams | undefined; | ||
private readonly cache: CachedURL<T>; | ||
private params?: ReadonlyURLSearchParams; | ||
public readonly source: string; | ||
public readonly base?: string; | ||
public get href(): T { | ||
return (this.share.href as T) | ||
??= this.share.url.href as T; | ||
return this.cache.href | ||
??= this.cache.url.href as T; | ||
} | ||
public get resource(): string { | ||
return this.share.resource | ||
??= this.href.slice(0, -this.fragment.length - this.query.length || this.href.length) + this.search; | ||
return this.cache.resource | ||
??= this.href.slice(0, this.href.search(/[?#]|$/)) + this.search; | ||
} | ||
public get origin(): string { | ||
return this.share.origin | ||
??= this.share.url.origin; | ||
return this.cache.origin | ||
??= this.cache.url.origin; | ||
} | ||
public get scheme(): string { | ||
return this.cache.scheme | ||
??= this.protocol.slice(0, -1); | ||
} | ||
public get protocol(): string { | ||
return this.share.protocol | ||
??= this.share.url.protocol; | ||
return this.cache.protocol | ||
??= this.cache.url.protocol; | ||
} | ||
public get username(): string { | ||
return this.share.username | ||
??= this.share.url.username; | ||
return this.cache.username | ||
??= this.cache.url.username; | ||
} | ||
public get password(): string { | ||
return this.share.password | ||
??= this.share.url.password; | ||
return this.cache.password | ||
??= this.cache.url.password; | ||
} | ||
public get host(): string { | ||
return this.share.host | ||
??= this.share.url.host; | ||
return this.cache.host | ||
??= this.cache.url.host; | ||
} | ||
public get hostname(): string { | ||
return this.share.hostname | ||
??= this.share.url.hostname; | ||
return this.cache.hostname | ||
??= this.cache.url.hostname; | ||
} | ||
public get port(): string { | ||
return this.share.port | ||
??= this.share.url.port; | ||
return this.cache.port | ||
??= this.cache.url.port; | ||
} | ||
public get path(): string { | ||
return this.share.path | ||
return this.cache.path | ||
??= `${this.pathname}${this.search}`; | ||
} | ||
public get pathname(): string { | ||
return this.share.pathname | ||
??= this.share.url.pathname; | ||
return this.cache.pathname | ||
??= this.cache.url.pathname; | ||
} | ||
public get search(): string { | ||
return this.share.search | ||
??= this.share.url.search; | ||
return this.cache.search | ||
??= this.cache.url.search; | ||
} | ||
public get query(): string { | ||
return this.share.query | ||
return this.cache.query | ||
??= this.search || this.href[this.href.length - this.fragment.length - 1] === '?' && '?' || ''; | ||
} | ||
public get hash(): string { | ||
return this.share.hash | ||
??= this.share.url.hash; | ||
return this.cache.hash | ||
??= this.cache.url.hash; | ||
} | ||
public get fragment(): string { | ||
return this.share.fragment | ||
return this.cache.fragment | ||
??= this.hash || this.href[this.href.length - 1] === '#' && '#' || ''; | ||
} | ||
public get searchParams(): URLSearchParams { | ||
public get searchParams(): ReadonlyURLSearchParams { | ||
return this.params | ||
??= new URLSearchParams(this.search); | ||
??= new ReadonlyURLSearchParams(this.search); | ||
} | ||
@@ -193,1 +196,22 @@ public toString(): string { | ||
} | ||
class ReadonlyURLSearchParams extends URLSearchParams { | ||
public override append(name: string, value: string): never { | ||
this.sort(); | ||
name; | ||
value; | ||
} | ||
public override delete(name: string, value?: string): never { | ||
this.sort(); | ||
name; | ||
value; | ||
} | ||
public override set(name: string, value: string): never { | ||
this.sort(); | ||
name; | ||
value; | ||
} | ||
public override sort(): never { | ||
throw new Error('Spica: URL: Cannot use mutable methods with ReadonlyURLSearchParams'); | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
619471
16905