diff --git a/.browserslistrc b/.browserslistrc index 284e01ff..d8e92c76 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -17,7 +17,7 @@ # Generated data. # -# Last generated Mar 22, 2024 7:11 AM UTC. +# Last generated Mar 22, 2024 7:58 AM UTC. [production] node >= 20.9.0 diff --git a/.dockerignore b/.dockerignore index f75cd1a8..81f36dd6 100644 --- a/.dockerignore +++ b/.dockerignore @@ -17,7 +17,7 @@ # Generated data. # -# Last generated Mar 22, 2024 7:11 AM UTC. +# Last generated Mar 22, 2024 7:58 AM UTC. # Locals diff --git a/.env.vault b/.env.vault index 386defa5..e473e13b 100644 --- a/.env.vault +++ b/.env.vault @@ -8,12 +8,12 @@ DOTENV_VAULT_MAIN="7GW98NfL4hM4N6HbfeDXCKreVxKvcVPupFRnZ8XJ+pomaQ==" DOTENV_VAULT_MAIN_VERSION=1 # dev -DOTENV_VAULT_DEV="a+hP6nwf4CqONa9HrqVRAS9dngtgA6QHT3l5D2DEHArCV934o/YKSUa7mJ4cah+U/cRf4bGUGgblBQqQW9vIWtMgDOOQA9E8Mf6NBX79DKJZOwODwrIYBfRHH6KcrD+NNOhOmSeJ5q+tado5J5Dyq3sBkWiQ7PkAgvEeMUnociUlfQSu+tXqXUJwm0zaY2PmCwanWeZMLFddlZTETwa+BvkPaOPM29XfeyJD/tUs9LRuqs7OKD7Qy+hM7ku2Nd3AIA==" -DOTENV_VAULT_DEV_VERSION=467 +DOTENV_VAULT_DEV="kSpcS87N4T+DP0j67/NEgV2VkFKSfZ0wtuR/JNYOGbnmYwCwIuqLefBaluLkYzZcwDMzh/GdbnM0fhxm8IpxeNI/wCR00nnCApNOt9nJN6aXA/XF8ebczvOvVW0k1FdH7qAVd/6+DtjO5+RH2/hX3cXhy9f9bZ4t1PT4J/c970H5xgaulwjwPOUY4X/APedGp0hRuLfqsDpkULk6yHwngyGvmJBQEwrOh6dEZqLJP4xleavS7ErfXSbF5oh861jy1Q==" +DOTENV_VAULT_DEV_VERSION=469 # ci -DOTENV_VAULT_CI="Qf4313mFcLvwmOisZB1rijt+IuiWsVk9z8k74kBCRWWLdfqRvNKZBSOWjzK9OuCwByPvgINYL4+8qr752as6FFMGFtXpLlOewG36R7JJv+caYksGYy/x0YJIEFGyIPHf1hlat/C2MVp1l+HF/T0bG9laB0yDKcKc84E10HNVHw==" -DOTENV_VAULT_CI_VERSION=467 +DOTENV_VAULT_CI="tSs3/1qDGDC5N/LUwefxIQMn/m/K+HOVxRhGsIegmRtVYLKU+TjrjyRhwyDoKjlNqZaOoihxsLbFqPL2Pj4ZaiuokBIbHzv7sgg6kqKm21nlAnYZ/cKfhAIf8iIe7Qud6fP0gHJ+3d9a83LgRlh0dvMO4D8CJGLspEbNjHgpIg==" +DOTENV_VAULT_CI_VERSION=469 # stage DOTENV_VAULT_STAGE="aRP8su2YV4jZu3w1HZ/SLaots0IwJDFw75TCpvXEFeNp7tw=" diff --git a/.gitattributes b/.gitattributes index 36ec66e3..0f82d93a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -17,7 +17,7 @@ # Generated data. # -# Last generated Mar 22, 2024 7:11 AM UTC. +# Last generated Mar 22, 2024 7:58 AM UTC. # Default diff --git a/.gitignore b/.gitignore index d0f926a7..fada611c 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,7 @@ # Generated data. # -# Last generated Mar 22, 2024 7:11 AM UTC. +# Last generated Mar 22, 2024 7:58 AM UTC. # Locals diff --git a/.npmignore b/.npmignore index 8b98a286..050051f8 100644 --- a/.npmignore +++ b/.npmignore @@ -25,7 +25,7 @@ # Generated data. # -# Last generated Mar 22, 2024 7:11 AM UTC. +# Last generated Mar 22, 2024 7:58 AM UTC. # Locals diff --git a/.prettierignore b/.prettierignore index 95af35f2..bacdb7c4 100644 --- a/.prettierignore +++ b/.prettierignore @@ -17,7 +17,7 @@ # Generated data. # -# Last generated Mar 22, 2024 7:11 AM UTC. +# Last generated Mar 22, 2024 7:58 AM UTC. # Packages diff --git a/.vscode/settings.json b/.vscode/settings.json index e38cbb4b..d8ff2c04 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,7 @@ * @note This entire file will be updated automatically. * @note Instead of editing here, please review `./settings.mjs`. * - * Last generated using `./settings.mjs` Mar 22, 2024 7:11 AM UTC. + * Last generated using `./settings.mjs` Mar 22, 2024 7:58 AM UTC. */ { "editor.formatOnType": false, diff --git a/.vscodeignore b/.vscodeignore index 594acae8..3c0e4d3c 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -17,7 +17,7 @@ # Generated data. # -# Last generated Mar 22, 2024 7:11 AM UTC. +# Last generated Mar 22, 2024 7:58 AM UTC. # Locals diff --git a/package-lock.json b/package-lock.json index 138843fc..8718c5e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@clevercanyon/utilities", - "version": "1.0.919", + "version": "1.0.920", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@clevercanyon/utilities", - "version": "1.0.919", + "version": "1.0.920", "cpu": [ "x64", "arm64" @@ -956,9 +956,9 @@ } }, "node_modules/@clevercanyon/utilities": { - "version": "1.0.919", - "resolved": "https://registry.npmjs.org/@clevercanyon/utilities/-/utilities-1.0.919.tgz", - "integrity": "sha512-sz/AOFGhNEVDEDEYWnSaxIghmD2c5bjPAjKbcbOcebRinRkE+PqNih+7sm7YV0/INhBg2r1EFTxJuDdqf+/b3A==", + "version": "1.0.920", + "resolved": "https://registry.npmjs.org/@clevercanyon/utilities/-/utilities-1.0.920.tgz", + "integrity": "sha512-oTQvebpzop49UCO2v+KQAUXjjq1U1CxuK6rkR090gI7S1Yf2uy6jwQw+ZhemKCPolLpt9rSZTS588GKXl/rHbQ==", "cpu": [ "x64", "arm64" diff --git a/package.json b/package.json index b4abfcd9..8d68be60 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "1.0.920", + "version": "1.0.921", "license": "GPL-3.0-or-later", "name": "@clevercanyon/utilities", "description": "Utilities for JavaScript apps running in any environment.", diff --git a/src/app.ts b/src/app.ts index f28096e3..7b485931 100644 --- a/src/app.ts +++ b/src/app.ts @@ -11,6 +11,8 @@ import { $brand, $env, $obj, $str, $time, $url, $user, type $type } from '#index * Defines types. */ export type BaseURLOptions = { parsed?: boolean }; +export type RootR2OriginURLOptions = { parsed?: boolean }; +export type RootR2BaseURLOptions = { parsed?: boolean }; export type R2OriginURLOptions = { parsed?: boolean }; export type R2BaseURLOptions = { parsed?: boolean }; @@ -115,6 +117,62 @@ export const baseURL = $fnꓺmemo( }, ); +/** + * Checks if environment has an app’s root R2 origin URL. + * + * @returns True if environment has an app’s root R2 origin URL. + */ +export const hasRootR2OriginURL = $fnꓺmemo((): boolean => { + return $env.get('APP_ROOT_R2_ORIGIN_URL') ? true : false; +}); + +/** + * Gets current app’s root R2 origin URL. + * + * @param options Options (all optional); {@see RootR2OriginURLOptions}. + * + * @returns Current app’s root R2 origin URL. + * + * @note Unable to deep freeze a URL, but we would do so if possible. + * For now, we just declare it readonly using a TypeScript return type. + */ +export const rootR2OriginURL = $fnꓺmemo( + { deep: true, maxSize: 2 }, + (options?: Options): Options extends RootR2OriginURLOptions & { parsed: true } ? $type.ReadonlyDeep<$type.URL> : string => { + const opts = $obj.defaults({}, options || {}, { parsed: false }) as Required, + value = $str.rTrim($env.get('APP_ROOT_R2_ORIGIN_URL', { type: 'string', require: true }), '/'); + return (opts.parsed ? $url.parse(value) : value) as ReturnType>; + }, +); + +/** + * Checks if environment has an app’s root R2 base URL. + * + * @returns True if environment has an app’s root R2 base URL. + */ +export const hasRootR2BaseURL = $fnꓺmemo((): boolean => { + return $env.get('APP_ROOT_R2_BASE_URL') || hasRootR2OriginURL() ? true : false; +}); + +/** + * Gets current app’s root R2 base URL. + * + * @param options Options (all optional); {@see RootR2BaseURLOptions}. + * + * @returns Current app’s root R2 base URL. + * + * @note Unable to deep freeze a URL, but we would do so if possible. + * For now, we just declare it readonly using a TypeScript return type. + */ +export const rootR2BaseURL = $fnꓺmemo( + { deep: true, maxSize: 2 }, + (options?: Options): Options extends RootR2BaseURLOptions & { parsed: true } ? $type.ReadonlyDeep<$type.URL> : string => { + const opts = $obj.defaults({}, options || {}, { parsed: false }) as Required, + value = $env.get('APP_ROOT_R2_BASE_URL', { type: 'string', default: '' }) || rootR2OriginURL() + '/app/' + pkgSlug() + '/'; + return (opts.parsed ? $url.parse(value) : value) as ReturnType>; + }, +); + /** * Checks if environment has an app’s R2 origin URL. * diff --git a/src/tests/brand/index.ts b/src/tests/brand/index.ts index f3f33b6e..90e87c3e 100644 --- a/src/tests/brand/index.ts +++ b/src/tests/brand/index.ts @@ -7,6 +7,8 @@ import { afterAll, beforeAll, describe, expect, test } from 'vitest'; const __origAppPkgName__ = $env.get('APP_PKG_NAME', { type: 'unknown' }); const __origAppBaseURL__ = $env.get('APP_BASE_URL', { type: 'unknown' }); +const __origAppRootR2OriginURL__ = $env.get('APP_ROOT_R2_ORIGIN_URL', { type: 'unknown' }); +const __origAppRootR2BaseURL__ = $env.get('APP_ROOT_R2_BASE_URL', { type: 'unknown' }); const __origAppR2OriginURL__ = $env.get('APP_R2_ORIGIN_URL', { type: 'unknown' }); const __origAppR2BaseURL__ = $env.get('APP_R2_BASE_URL', { type: 'unknown' }); const __origAppBrandProps__ = $env.get('APP_BRAND_PROPS', { type: 'unknown' }); @@ -16,6 +18,8 @@ describe('$brand', async () => { beforeAll(async () => { $env.set('APP_PKG_NAME', '@clevercanyon/x.tld'); $env.set('APP_BASE_URL', 'https://x.tld/base/'); + $env.set('APP_ROOT_R2_ORIGIN_URL', 'https://r2.tld'); + $env.set('APP_ROOT_R2_BASE_URL', 'https://r2.tld/base/'); $env.set('APP_R2_ORIGIN_URL', 'https://r2.tld'); $env.set('APP_R2_BASE_URL', 'https://r2.tld/base/'); $env.set('APP_BRAND_PROPS', { type: 'site' }); @@ -24,6 +28,8 @@ describe('$brand', async () => { afterAll(async () => { $env.set('APP_PKG_NAME', __origAppPkgName__); $env.set('APP_BASE_URL', __origAppBaseURL__); + $env.set('APP_ROOT_R2_ORIGIN_URL', __origAppRootR2OriginURL__); + $env.set('APP_ROOT_R2_BASE_URL', __origAppRootR2BaseURL__); $env.set('APP_R2_ORIGIN_URL', __origAppR2OriginURL__); $env.set('APP_R2_BASE_URL', __origAppR2BaseURL__); $env.set('APP_BRAND_PROPS', __origAppBrandProps__); @@ -37,6 +43,12 @@ describe('$brand', async () => { $app.hasBaseURL.flush(), $app.baseURL.flush(), // + $app.hasRootR2OriginURL.flush(), + $app.rootR2OriginURL.flush(), + // + $app.hasRootR2BaseURL.flush(), + $app.rootR2BaseURL.flush(), + // $app.hasR2OriginURL.flush(), $app.r2OriginURL.flush(), // diff --git a/src/tests/classes/logger/index.ts b/src/tests/classes/logger/index.ts index 4209b654..c14b7a54 100644 --- a/src/tests/classes/logger/index.ts +++ b/src/tests/classes/logger/index.ts @@ -7,6 +7,8 @@ import { afterAll, beforeAll, describe, expect, test } from 'vitest'; const __origAppPkgName__ = $env.get('APP_PKG_NAME', { type: 'unknown' }); const __origAppBaseURL__ = $env.get('APP_BASE_URL', { type: 'unknown' }); +const __origAppRootR2OriginURL__ = $env.get('APP_ROOT_R2_ORIGIN_URL', { type: 'unknown' }); +const __origAppRootR2BaseURL__ = $env.get('APP_ROOT_R2_BASE_URL', { type: 'unknown' }); const __origAppR2OriginURL__ = $env.get('APP_R2_ORIGIN_URL', { type: 'unknown' }); const __origAppR2BaseURL__ = $env.get('APP_R2_BASE_URL', { type: 'unknown' }); const __origAppBrandProps__ = $env.get('APP_BRAND_PROPS', { type: 'unknown' }); @@ -19,6 +21,8 @@ describe('Logger', async () => { beforeAll(async () => { $env.set('APP_PKG_NAME', '@clevercanyon/x.tld'); $env.set('APP_BASE_URL', 'https://x.tld/base/'); + $env.set('APP_ROOT_R2_ORIGIN_URL', 'https://r2.tld'); + $env.set('APP_ROOT_R2_BASE_URL', 'https://r2.tld/base/'); $env.set('APP_R2_ORIGIN_URL', 'https://r2.tld'); $env.set('APP_R2_BASE_URL', 'https://r2.tld/base/'); $env.set('APP_BRAND_PROPS', { type: 'site' }); @@ -27,6 +31,8 @@ describe('Logger', async () => { afterAll(async () => { $env.set('APP_PKG_NAME', __origAppPkgName__); $env.set('APP_BASE_URL', __origAppBaseURL__); + $env.set('APP_ROOT_R2_ORIGIN_URL', __origAppRootR2OriginURL__); + $env.set('APP_ROOT_R2_BASE_URL', __origAppRootR2BaseURL__); $env.set('APP_R2_ORIGIN_URL', __origAppR2OriginURL__); $env.set('APP_R2_BASE_URL', __origAppR2BaseURL__); $env.set('APP_BRAND_PROPS', __origAppBrandProps__); @@ -40,6 +46,12 @@ describe('Logger', async () => { $app.hasBaseURL.flush(), $app.baseURL.flush(), // + $app.hasRootR2OriginURL.flush(), + $app.rootR2OriginURL.flush(), + // + $app.hasRootR2BaseURL.flush(), + $app.rootR2BaseURL.flush(), + // $app.hasR2OriginURL.flush(), $app.r2OriginURL.flush(), // diff --git a/src/tests/preact/apis/iso/hydratively-render-spa.web.tsx b/src/tests/preact/apis/iso/hydratively-render-spa.web.tsx index 102dc934..50d3d523 100644 --- a/src/tests/preact/apis/iso/hydratively-render-spa.web.tsx +++ b/src/tests/preact/apis/iso/hydratively-render-spa.web.tsx @@ -8,6 +8,8 @@ import { afterAll, beforeAll, describe, expect, test, vi } from 'vitest'; const __origAppPkgName__ = $env.get('APP_PKG_NAME', { type: 'unknown' }); const __origAppBaseURL__ = $env.get('APP_BASE_URL', { type: 'unknown' }); +const __origAppRootR2OriginURL__ = $env.get('APP_ROOT_R2_ORIGIN_URL', { type: 'unknown' }); +const __origAppRootR2BaseURL__ = $env.get('APP_ROOT_R2_BASE_URL', { type: 'unknown' }); const __origAppR2OriginURL__ = $env.get('APP_R2_ORIGIN_URL', { type: 'unknown' }); const __origAppR2BaseURL__ = $env.get('APP_R2_BASE_URL', { type: 'unknown' }); const __origAppBrandProps__ = $env.get('APP_BRAND_PROPS', { type: 'unknown' }); @@ -17,6 +19,8 @@ describe('$preact.iso.hydrativelyRenderSPA() [web]', async () => { beforeAll(async () => { $env.set('APP_PKG_NAME', '@clevercanyon/x.tld'); $env.set('APP_BASE_URL', 'https://x.tld/base/'); + $env.set('APP_ROOT_R2_ORIGIN_URL', 'https://r2.tld'); + $env.set('APP_ROOT_R2_BASE_URL', 'https://r2.tld/base/'); $env.set('APP_R2_ORIGIN_URL', 'https://r2.tld'); $env.set('APP_R2_BASE_URL', 'https://r2.tld/base/'); $env.set('APP_BRAND_PROPS', { type: 'site' }); @@ -25,6 +29,8 @@ describe('$preact.iso.hydrativelyRenderSPA() [web]', async () => { afterAll(async () => { $env.set('APP_PKG_NAME', __origAppPkgName__); $env.set('APP_BASE_URL', __origAppBaseURL__); + $env.set('APP_ROOT_R2_ORIGIN_URL', __origAppRootR2OriginURL__); + $env.set('APP_ROOT_R2_BASE_URL', __origAppRootR2BaseURL__); $env.set('APP_R2_ORIGIN_URL', __origAppR2OriginURL__); $env.set('APP_R2_BASE_URL', __origAppR2BaseURL__); $env.set('APP_BRAND_PROPS', __origAppBrandProps__); @@ -38,6 +44,12 @@ describe('$preact.iso.hydrativelyRenderSPA() [web]', async () => { $app.hasBaseURL.flush(), $app.baseURL.flush(), // + $app.hasRootR2OriginURL.flush(), + $app.rootR2OriginURL.flush(), + // + $app.hasRootR2BaseURL.flush(), + $app.rootR2BaseURL.flush(), + // $app.hasR2OriginURL.flush(), $app.r2OriginURL.flush(), // diff --git a/src/tests/preact/apis/iso/prerender-spa-404.cfw.tsx b/src/tests/preact/apis/iso/prerender-spa-404.cfw.tsx index 7d38d8d7..007070fd 100644 --- a/src/tests/preact/apis/iso/prerender-spa-404.cfw.tsx +++ b/src/tests/preact/apis/iso/prerender-spa-404.cfw.tsx @@ -8,6 +8,8 @@ import { afterAll, beforeAll, describe, expect, test } from 'vitest'; const __origAppPkgName__ = $env.get('APP_PKG_NAME', { type: 'unknown' }); const __origAppBaseURL__ = $env.get('APP_BASE_URL', { type: 'unknown' }); +const __origAppRootR2OriginURL__ = $env.get('APP_ROOT_R2_ORIGIN_URL', { type: 'unknown' }); +const __origAppRootR2BaseURL__ = $env.get('APP_ROOT_R2_BASE_URL', { type: 'unknown' }); const __origAppR2OriginURL__ = $env.get('APP_R2_ORIGIN_URL', { type: 'unknown' }); const __origAppR2BaseURL__ = $env.get('APP_R2_BASE_URL', { type: 'unknown' }); const __origAppBrandProps__ = $env.get('APP_BRAND_PROPS', { type: 'unknown' }); @@ -17,6 +19,8 @@ describe('$preact.iso.prerenderSPA() [404-cfw]', async () => { beforeAll(async () => { $env.set('APP_PKG_NAME', '@clevercanyon/x.tld'); $env.set('APP_BASE_URL', 'https://x.tld/base/'); + $env.set('APP_ROOT_R2_ORIGIN_URL', 'https://r2.tld'); + $env.set('APP_ROOT_R2_BASE_URL', 'https://r2.tld/base/'); $env.set('APP_R2_ORIGIN_URL', 'https://r2.tld'); $env.set('APP_R2_BASE_URL', 'https://r2.tld/base/'); $env.set('APP_BRAND_PROPS', { type: 'site' }); @@ -25,6 +29,8 @@ describe('$preact.iso.prerenderSPA() [404-cfw]', async () => { afterAll(async () => { $env.set('APP_PKG_NAME', __origAppPkgName__); $env.set('APP_BASE_URL', __origAppBaseURL__); + $env.set('APP_ROOT_R2_ORIGIN_URL', __origAppRootR2OriginURL__); + $env.set('APP_ROOT_R2_BASE_URL', __origAppRootR2BaseURL__); $env.set('APP_R2_ORIGIN_URL', __origAppR2OriginURL__); $env.set('APP_R2_BASE_URL', __origAppR2BaseURL__); $env.set('APP_BRAND_PROPS', __origAppBrandProps__); @@ -38,6 +44,12 @@ describe('$preact.iso.prerenderSPA() [404-cfw]', async () => { $app.hasBaseURL.flush(), $app.baseURL.flush(), // + $app.hasRootR2OriginURL.flush(), + $app.rootR2OriginURL.flush(), + // + $app.hasRootR2BaseURL.flush(), + $app.rootR2BaseURL.flush(), + // $app.hasR2OriginURL.flush(), $app.r2OriginURL.flush(), // diff --git a/src/tests/preact/apis/iso/prerender-spa-for-web-fixture.tsx b/src/tests/preact/apis/iso/prerender-spa-for-web-fixture.tsx index e2f2c99c..347152bd 100644 --- a/src/tests/preact/apis/iso/prerender-spa-for-web-fixture.tsx +++ b/src/tests/preact/apis/iso/prerender-spa-for-web-fixture.tsx @@ -12,6 +12,8 @@ import { afterAll, beforeAll, describe, expect, test } from 'vitest'; const __origAppPkgName__ = $env.get('APP_PKG_NAME', { type: 'unknown' }); const __origAppBaseURL__ = $env.get('APP_BASE_URL', { type: 'unknown' }); +const __origAppRootR2OriginURL__ = $env.get('APP_ROOT_R2_ORIGIN_URL', { type: 'unknown' }); +const __origAppRootR2BaseURL__ = $env.get('APP_ROOT_R2_BASE_URL', { type: 'unknown' }); const __origAppR2OriginURL__ = $env.get('APP_R2_ORIGIN_URL', { type: 'unknown' }); const __origAppR2BaseURL__ = $env.get('APP_R2_BASE_URL', { type: 'unknown' }); const __origAppBrandProps__ = $env.get('APP_BRAND_PROPS', { type: 'unknown' }); @@ -21,6 +23,8 @@ describe('$preact.iso.prerenderSPA() [web-fixture]', async () => { beforeAll(async () => { $env.set('APP_PKG_NAME', '@clevercanyon/x.tld'); $env.set('APP_BASE_URL', 'https://x.tld/base/'); + $env.set('APP_ROOT_R2_ORIGIN_URL', 'https://r2.tld'); + $env.set('APP_ROOT_R2_BASE_URL', 'https://r2.tld/base/'); $env.set('APP_R2_ORIGIN_URL', 'https://r2.tld'); $env.set('APP_R2_BASE_URL', 'https://r2.tld/base/'); $env.set('APP_BRAND_PROPS', { type: 'site' }); @@ -29,6 +33,8 @@ describe('$preact.iso.prerenderSPA() [web-fixture]', async () => { afterAll(async () => { $env.set('APP_PKG_NAME', __origAppPkgName__); $env.set('APP_BASE_URL', __origAppBaseURL__); + $env.set('APP_ROOT_R2_ORIGIN_URL', __origAppRootR2OriginURL__); + $env.set('APP_ROOT_R2_BASE_URL', __origAppRootR2BaseURL__); $env.set('APP_R2_ORIGIN_URL', __origAppR2OriginURL__); $env.set('APP_R2_BASE_URL', __origAppR2BaseURL__); $env.set('APP_BRAND_PROPS', __origAppBrandProps__); @@ -42,6 +48,12 @@ describe('$preact.iso.prerenderSPA() [web-fixture]', async () => { $app.hasBaseURL.flush(), $app.baseURL.flush(), // + $app.hasRootR2OriginURL.flush(), + $app.rootR2OriginURL.flush(), + // + $app.hasRootR2BaseURL.flush(), + $app.rootR2BaseURL.flush(), + // $app.hasR2OriginURL.flush(), $app.r2OriginURL.flush(), // diff --git a/src/tests/preact/apis/iso/prerender-spa-lazy.cfw.tsx b/src/tests/preact/apis/iso/prerender-spa-lazy.cfw.tsx index 69a741ec..d4073d6c 100644 --- a/src/tests/preact/apis/iso/prerender-spa-lazy.cfw.tsx +++ b/src/tests/preact/apis/iso/prerender-spa-lazy.cfw.tsx @@ -8,6 +8,8 @@ import { afterAll, beforeAll, describe, expect, test, vi } from 'vitest'; const __origAppPkgName__ = $env.get('APP_PKG_NAME', { type: 'unknown' }); const __origAppBaseURL__ = $env.get('APP_BASE_URL', { type: 'unknown' }); +const __origAppRootR2OriginURL__ = $env.get('APP_ROOT_R2_ORIGIN_URL', { type: 'unknown' }); +const __origAppRootR2BaseURL__ = $env.get('APP_ROOT_R2_BASE_URL', { type: 'unknown' }); const __origAppR2OriginURL__ = $env.get('APP_R2_ORIGIN_URL', { type: 'unknown' }); const __origAppR2BaseURL__ = $env.get('APP_R2_BASE_URL', { type: 'unknown' }); const __origAppBrandProps__ = $env.get('APP_BRAND_PROPS', { type: 'unknown' }); @@ -17,6 +19,8 @@ describe('$preact.iso.prerenderSPA() [lazy-cfw]', async () => { beforeAll(async () => { $env.set('APP_PKG_NAME', '@clevercanyon/x.tld'); $env.set('APP_BASE_URL', 'https://x.tld/base/'); + $env.set('APP_ROOT_R2_ORIGIN_URL', 'https://r2.tld'); + $env.set('APP_ROOT_R2_BASE_URL', 'https://r2.tld/base/'); $env.set('APP_R2_ORIGIN_URL', 'https://r2.tld'); $env.set('APP_R2_BASE_URL', 'https://r2.tld/base/'); $env.set('APP_BRAND_PROPS', { type: 'site' }); @@ -25,6 +29,8 @@ describe('$preact.iso.prerenderSPA() [lazy-cfw]', async () => { afterAll(async () => { $env.set('APP_PKG_NAME', __origAppPkgName__); $env.set('APP_BASE_URL', __origAppBaseURL__); + $env.set('APP_ROOT_R2_ORIGIN_URL', __origAppRootR2OriginURL__); + $env.set('APP_ROOT_R2_BASE_URL', __origAppRootR2BaseURL__); $env.set('APP_R2_ORIGIN_URL', __origAppR2OriginURL__); $env.set('APP_R2_BASE_URL', __origAppR2BaseURL__); $env.set('APP_BRAND_PROPS', __origAppBrandProps__); @@ -38,6 +44,12 @@ describe('$preact.iso.prerenderSPA() [lazy-cfw]', async () => { $app.hasBaseURL.flush(), $app.baseURL.flush(), // + $app.hasRootR2OriginURL.flush(), + $app.rootR2OriginURL.flush(), + // + $app.hasRootR2BaseURL.flush(), + $app.rootR2BaseURL.flush(), + // $app.hasR2OriginURL.flush(), $app.r2OriginURL.flush(), // diff --git a/src/tests/preact/apis/iso/prerender-spa.node.tsx b/src/tests/preact/apis/iso/prerender-spa.node.tsx index 53d33d13..ebda0a69 100644 --- a/src/tests/preact/apis/iso/prerender-spa.node.tsx +++ b/src/tests/preact/apis/iso/prerender-spa.node.tsx @@ -8,6 +8,8 @@ import { afterAll, beforeAll, describe, expect, test } from 'vitest'; const __origAppPkgName__ = $env.get('APP_PKG_NAME', { type: 'unknown' }); const __origAppBaseURL__ = $env.get('APP_BASE_URL', { type: 'unknown' }); +const __origAppRootR2OriginURL__ = $env.get('APP_ROOT_R2_ORIGIN_URL', { type: 'unknown' }); +const __origAppRootR2BaseURL__ = $env.get('APP_ROOT_R2_BASE_URL', { type: 'unknown' }); const __origAppR2OriginURL__ = $env.get('APP_R2_ORIGIN_URL', { type: 'unknown' }); const __origAppR2BaseURL__ = $env.get('APP_R2_BASE_URL', { type: 'unknown' }); const __origAppBrandProps__ = $env.get('APP_BRAND_PROPS', { type: 'unknown' }); @@ -17,6 +19,8 @@ describe('$preact.iso.prerenderSPA()', async () => { beforeAll(async () => { $env.set('APP_PKG_NAME', '@clevercanyon/x.tld'); $env.set('APP_BASE_URL', 'https://x.tld/base/'); + $env.set('APP_ROOT_R2_ORIGIN_URL', 'https://r2.tld'); + $env.set('APP_ROOT_R2_BASE_URL', 'https://r2.tld/base/'); $env.set('APP_R2_ORIGIN_URL', 'https://r2.tld'); $env.set('APP_R2_BASE_URL', 'https://r2.tld/base/'); $env.set('APP_BRAND_PROPS', { type: 'site' }); @@ -25,6 +29,8 @@ describe('$preact.iso.prerenderSPA()', async () => { afterAll(async () => { $env.set('APP_PKG_NAME', __origAppPkgName__); $env.set('APP_BASE_URL', __origAppBaseURL__); + $env.set('APP_ROOT_R2_ORIGIN_URL', __origAppRootR2OriginURL__); + $env.set('APP_ROOT_R2_BASE_URL', __origAppRootR2BaseURL__); $env.set('APP_R2_ORIGIN_URL', __origAppR2OriginURL__); $env.set('APP_R2_BASE_URL', __origAppR2BaseURL__); $env.set('APP_BRAND_PROPS', __origAppBrandProps__); @@ -38,6 +44,12 @@ describe('$preact.iso.prerenderSPA()', async () => { $app.hasBaseURL.flush(), $app.baseURL.flush(), // + $app.hasRootR2OriginURL.flush(), + $app.rootR2OriginURL.flush(), + // + $app.hasRootR2BaseURL.flush(), + $app.rootR2BaseURL.flush(), + // $app.hasR2OriginURL.flush(), $app.r2OriginURL.flush(), // diff --git a/src/tests/preact/components/root.tsx b/src/tests/preact/components/root.tsx index 23a6a5c1..5c7cf7ec 100644 --- a/src/tests/preact/components/root.tsx +++ b/src/tests/preact/components/root.tsx @@ -8,6 +8,8 @@ import { afterAll, beforeAll, describe, expect, test } from 'vitest'; const __origAppPkgName__ = $env.get('APP_PKG_NAME', { type: 'unknown' }); const __origAppBaseURL__ = $env.get('APP_BASE_URL', { type: 'unknown' }); +const __origAppRootR2OriginURL__ = $env.get('APP_ROOT_R2_ORIGIN_URL', { type: 'unknown' }); +const __origAppRootR2BaseURL__ = $env.get('APP_ROOT_R2_BASE_URL', { type: 'unknown' }); const __origAppR2OriginURL__ = $env.get('APP_R2_ORIGIN_URL', { type: 'unknown' }); const __origAppR2BaseURL__ = $env.get('APP_R2_BASE_URL', { type: 'unknown' }); const __origAppBrandProps__ = $env.get('APP_BRAND_PROPS', { type: 'unknown' }); @@ -17,6 +19,8 @@ describe('', async () => { beforeAll(async () => { $env.set('APP_PKG_NAME', '@clevercanyon/x.tld'); $env.set('APP_BASE_URL', 'https://x.tld/base/'); + $env.set('APP_ROOT_R2_ORIGIN_URL', 'https://r2.tld'); + $env.set('APP_ROOT_R2_BASE_URL', 'https://r2.tld/base/'); $env.set('APP_R2_ORIGIN_URL', 'https://r2.tld'); $env.set('APP_R2_BASE_URL', 'https://r2.tld/base/'); $env.set('APP_BRAND_PROPS', { type: 'site' }); @@ -25,6 +29,8 @@ describe('', async () => { afterAll(async () => { $env.set('APP_PKG_NAME', __origAppPkgName__); $env.set('APP_BASE_URL', __origAppBaseURL__); + $env.set('APP_ROOT_R2_ORIGIN_URL', __origAppRootR2OriginURL__); + $env.set('APP_ROOT_R2_BASE_URL', __origAppRootR2BaseURL__); $env.set('APP_R2_ORIGIN_URL', __origAppR2OriginURL__); $env.set('APP_R2_BASE_URL', __origAppR2BaseURL__); $env.set('APP_BRAND_PROPS', __origAppBrandProps__); @@ -38,6 +44,12 @@ describe('', async () => { $app.hasBaseURL.flush(), $app.baseURL.flush(), // + $app.hasRootR2OriginURL.flush(), + $app.rootR2OriginURL.flush(), + // + $app.hasRootR2BaseURL.flush(), + $app.rootR2BaseURL.flush(), + // $app.hasR2OriginURL.flush(), $app.r2OriginURL.flush(), // diff --git a/src/tests/url/index.any.ts b/src/tests/url/index.any.ts index ca566c00..e2f7bcbf 100644 --- a/src/tests/url/index.any.ts +++ b/src/tests/url/index.any.ts @@ -7,6 +7,8 @@ import { afterAll, beforeAll, describe, expect, test } from 'vitest'; const __origAppPkgName__ = $env.get('APP_PKG_NAME', { type: 'unknown' }); const __origAppBaseURL__ = $env.get('APP_BASE_URL', { type: 'unknown' }); +const __origAppRootR2OriginURL__ = $env.get('APP_ROOT_R2_ORIGIN_URL', { type: 'unknown' }); +const __origAppRootR2BaseURL__ = $env.get('APP_ROOT_R2_BASE_URL', { type: 'unknown' }); const __origAppR2OriginURL__ = $env.get('APP_R2_ORIGIN_URL', { type: 'unknown' }); const __origAppR2BaseURL__ = $env.get('APP_R2_BASE_URL', { type: 'unknown' }); const __origAppBrandProps__ = $env.get('APP_BRAND_PROPS', { type: 'unknown' }); @@ -16,6 +18,8 @@ describe('$url', async () => { beforeAll(async () => { $env.set('APP_PKG_NAME', '@clevercanyon/x.tld'); $env.set('APP_BASE_URL', 'https://x.tld/base/'); + $env.set('APP_ROOT_R2_ORIGIN_URL', 'https://r2.tld'); + $env.set('APP_ROOT_R2_BASE_URL', 'https://r2.tld/base/'); $env.set('APP_R2_ORIGIN_URL', 'https://r2.tld'); $env.set('APP_R2_BASE_URL', 'https://r2.tld/base/'); $env.set('APP_BRAND_PROPS', { type: 'site' }); @@ -24,6 +28,8 @@ describe('$url', async () => { afterAll(async () => { $env.set('APP_PKG_NAME', __origAppPkgName__); $env.set('APP_BASE_URL', __origAppBaseURL__); + $env.set('APP_ROOT_R2_ORIGIN_URL', __origAppRootR2OriginURL__); + $env.set('APP_ROOT_R2_BASE_URL', __origAppRootR2BaseURL__); $env.set('APP_R2_ORIGIN_URL', __origAppR2OriginURL__); $env.set('APP_R2_BASE_URL', __origAppR2BaseURL__); $env.set('APP_BRAND_PROPS', __origAppBrandProps__); @@ -37,6 +43,12 @@ describe('$url', async () => { $app.hasBaseURL.flush(), $app.baseURL.flush(), // + $app.hasRootR2OriginURL.flush(), + $app.rootR2OriginURL.flush(), + // + $app.hasRootR2BaseURL.flush(), + $app.rootR2BaseURL.flush(), + // $app.hasR2OriginURL.flush(), $app.r2OriginURL.flush(), // diff --git a/src/url.ts b/src/url.ts index 96ad1f00..b11c683d 100644 --- a/src/url.ts +++ b/src/url.ts @@ -388,6 +388,127 @@ function _removeAppBasePath(parseable: $type.URL | string): Readonly<$type.URL> } export const removeAppBasePath = $fnꓺmemo(24, _removeAppBasePath); +/* --- + * App root R2 origin utilities. + */ + +/** + * Gets app’s root R2 origin URL. + * + * @returns App’s root R2 origin URL. + * + * @see $app.rootR2OriginURL() + */ + +/** + * Gets URL from app’s root R2 origin. + * + * @param parseable Parseable URL or string. + * + * @returns A full URL from app’s root R2 origin. + */ +export const fromAppRootR2Origin = $fnꓺmemo(24, (parseable: $type.URL | string): string => { + return parse(parseable, $app.rootR2OriginURL() + '/').toString(); +}); + +/** + * Gets root-relative path from app’s root R2 origin. + * + * @param parseable Parseable URL or string. + * + * @returns `/base/path?query#hash` from app’s root R2 origin. + */ +export const pathFromAppRootR2Origin = $fnꓺmemo(24, (parseable: $type.URL | string): string => { + return toPathQueryHash(fromAppRootR2Origin(parseable)); +}); + +/* --- + * App root R2 base utilities. + */ + +/** + * Gets app’s root R2 base URL. + * + * @returns App’s root R2 base URL. + * + * @see $app.rootR2BaseURL() + */ + +/** + * Gets app’s root R2 base path. + * + * @returns App’s root R2 base path. + */ +export const appRootR2BasePath = $fnꓺmemo((): string => { + return parse($app.rootR2BaseURL()).pathname; +}); + +/** + * Gets URL from app’s root R2 base. + * + * @param parseable Parseable URL or string. + * + * @returns A full URL from app’s root R2 base. + */ +export const fromAppRootR2Base = $fnꓺmemo(24, (parseable: $type.URL | string): string => { + return parse(parseable, $app.rootR2BaseURL()).toString(); +}); + +/** + * Gets root-relative path from app’s root R2 base. + * + * @param parseable Parseable URL or string. + * + * @returns `/base/path?query#hash` from app’s root R2 base. + */ +export const pathFromAppRootR2Base = $fnꓺmemo(24, (parseable: $type.URL | string): string => { + return toPathQueryHash(fromAppRootR2Base(parseable)); +}); + +/** + * Adds app’s root R2 base path. + * + * @param parseable Parseable URL or string. + * + * @returns Parseable URL or string with app’s root R2 base path. + * + * - Returns a {@see URL} if input was a {@see URL}. A string otherwise. + * + * @note Unable to deep freeze a URL, but we would do so if possible. + * For now, we just declare it readonly using a TypeScript return type. + * + * @see addBasePath() + */ +function _addAppRootR2BasePath(parseable: $type.URL): Readonly<$type.URL>; +function _addAppRootR2BasePath(parseable: string): string; + +function _addAppRootR2BasePath(parseable: $type.URL | string): Readonly<$type.URL> | string { + return addBasePath(parseable, $app.rootR2BaseURL()); +} +export const addAppRootR2BasePath = $fnꓺmemo(24, _addAppRootR2BasePath); + +/** + * Removes app’s root R2 base path. + * + * @param parseable Parseable URL or string. + * + * @returns Parseable URL or string without app’s root R2 base path. + * + * - Returns a {@see URL} if input was a {@see URL}. A string otherwise. + * + * @note Unable to deep freeze a URL, but we would do so if possible. + * For now, we just declare it readonly using a TypeScript return type. + * + * @see removeBasePath() + */ +function _removeAppRootR2BasePath(parseable: $type.URL): Readonly<$type.URL>; +function _removeAppRootR2BasePath(parseable: string): string; + +function _removeAppRootR2BasePath(parseable: $type.URL | string): Readonly<$type.URL> | string { + return removeBasePath(parseable, $app.rootR2BaseURL()); +} +export const removeAppRootR2BasePath = $fnꓺmemo(24, _removeAppRootR2BasePath); + /* --- * App R2 origin utilities. */ diff --git a/tsconfig.json b/tsconfig.json index 5973d1cb..a5b1d6d7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ * @note This entire file will be updated automatically. * @note Instead of editing here, please review `./tsconfig.mjs`. * - * Last generated using `./tsconfig.mjs` Mar 22, 2024 7:11 AM UTC. + * Last generated using `./tsconfig.mjs` Mar 22, 2024 7:58 AM UTC. */ { "include": ["./src/**/*", "./dev-types.d.ts"], diff --git a/wrangler.toml b/wrangler.toml index 7a65a1b1..40387ea6 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -7,7 +7,7 @@ # @note This entire file will be updated automatically. # @note Instead of editing here, please review `./wrangler.mjs`. # -# Last generated using `./wrangler.mjs` Mar 22, 2024 7:11 AM UTC. +# Last generated using `./wrangler.mjs` Mar 22, 2024 7:58 AM UTC. ## send_metrics = false