@sneat/core 0.16.0 → 0.17.0
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/esm2022/lib/index.js +1 -0
- package/esm2022/lib/index.js.map +1 -1
- package/esm2022/lib/reserved-public-path.js +84 -0
- package/esm2022/lib/reserved-public-path.js.map +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/reserved-public-path.d.ts +42 -0
- package/package.json +1 -1
- package/tsconfig.lib.prod.tsbuildinfo +1 -1
package/esm2022/lib/index.js
CHANGED
|
@@ -14,6 +14,7 @@ export * from './sneat-extensions';
|
|
|
14
14
|
export * from './form-field';
|
|
15
15
|
export * from './team-type';
|
|
16
16
|
export * from './location-href';
|
|
17
|
+
export * from './reserved-public-path';
|
|
17
18
|
export * from './interfaces';
|
|
18
19
|
export * from './current-space-storage';
|
|
19
20
|
export * from './directives';
|
package/esm2022/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../libs/core/src/lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,OAAO,CAAC;AACtB,cAAc,SAAS,CAAC;AACxB,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,MAAM,CAAC;AACrB,cAAc,SAAS,CAAC;AACxB,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC","sourcesContent":["export * from './animations';\nexport * from './app.service';\nexport * from './core-models';\nexport * from './nav';\nexport * from './store';\nexport * from './sneat-enum-keys';\nexport * from './services';\nexport * from './exclude-undefined';\nexport * from './logging';\nexport * from './utils';\nexport * from './eq';\nexport * from './types';\nexport * from './sneat-extensions';\nexport * from './form-field';\nexport * from './team-type';\nexport * from './location-href';\nexport * from './interfaces';\nexport * from './current-space-storage';\nexport * from './directives';\nexport * from './environment-config';\nexport * from './logging/interfaces';\nexport * from './analytics.interface';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../libs/core/src/lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,OAAO,CAAC;AACtB,cAAc,SAAS,CAAC;AACxB,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,MAAM,CAAC;AACrB,cAAc,SAAS,CAAC;AACxB,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC","sourcesContent":["export * from './animations';\nexport * from './app.service';\nexport * from './core-models';\nexport * from './nav';\nexport * from './store';\nexport * from './sneat-enum-keys';\nexport * from './services';\nexport * from './exclude-undefined';\nexport * from './logging';\nexport * from './utils';\nexport * from './eq';\nexport * from './types';\nexport * from './sneat-extensions';\nexport * from './form-field';\nexport * from './team-type';\nexport * from './location-href';\nexport * from './reserved-public-path';\nexport * from './interfaces';\nexport * from './current-space-storage';\nexport * from './directives';\nexport * from './environment-config';\nexport * from './logging/interfaces';\nexport * from './analytics.interface';\n"]}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Reserved public paths for the standard Sneat routing model: the Angular
|
|
2
|
+
// application is mounted at the ROOT (`/`), and a small, fixed set of paths is
|
|
3
|
+
// reserved for the public landing/content surface. The edge (Cloudflare Worker)
|
|
4
|
+
// uses this to split traffic:
|
|
5
|
+
//
|
|
6
|
+
// if (isReservedPublicPath(pathname)) return serveLanding(request)
|
|
7
|
+
// return serveApplication(request) // root-mounted Angular SPA
|
|
8
|
+
//
|
|
9
|
+
// See docs/extension-standards/routing-and-deployment.md for the full contract.
|
|
10
|
+
/**
|
|
11
|
+
* Locale codes whose `/{locale}/*` subtree is reserved for landing/content
|
|
12
|
+
* pages. Two-letter ISO-639-1 codes.
|
|
13
|
+
*
|
|
14
|
+
* This is an explicit ALLOW-LIST, not an "any two-letter segment" rule, and
|
|
15
|
+
* that distinction is load-bearing: `/my` (the personal-dashboard section) is
|
|
16
|
+
* also two letters. Matching "any two letters" would swallow `/my/assets` as if
|
|
17
|
+
* `my` were a locale. With an allow-list, unknown two-letter segments fall
|
|
18
|
+
* through to the application (fail-closed): `/my/assets` → app, `/en/about` →
|
|
19
|
+
* landing.
|
|
20
|
+
*/
|
|
21
|
+
export const DEFAULT_RESERVED_LOCALES = ['en'];
|
|
22
|
+
/**
|
|
23
|
+
* Returns `true` when `pathname` belongs to the public landing/content surface
|
|
24
|
+
* rather than the root-mounted Angular application.
|
|
25
|
+
*
|
|
26
|
+
* Reserved:
|
|
27
|
+
* - `/` — root landing page
|
|
28
|
+
* - `/{locale}` and `/{locale}/*` — localized content/marketing/legal/docs/SEO
|
|
29
|
+
* (locale ∈ the allow-list; see {@link ReservedPublicPathOptions.locales})
|
|
30
|
+
* - `/static` and `/static/*` — shared static files
|
|
31
|
+
* - `/.well-known` and `/.well-known/*` — well-known files
|
|
32
|
+
* - `/robots.txt`
|
|
33
|
+
* - `/sitemap.xml`, `/sitemap-*.xml`, `/sitemap-index.xml`
|
|
34
|
+
* - `/favicon.ico`, `/favicon.svg`
|
|
35
|
+
* - `/manifest.webmanifest`
|
|
36
|
+
*
|
|
37
|
+
* Everything else (e.g. `/`, `/space/...`, `/my/assets`, `/event/abc`,
|
|
38
|
+
* `/u/alex`) is an application route.
|
|
39
|
+
*
|
|
40
|
+
* Query strings and fragments are ignored — pass a bare pathname (a full URL is
|
|
41
|
+
* tolerated and reduced to its pathname).
|
|
42
|
+
*/
|
|
43
|
+
export function isReservedPublicPath(pathname, options) {
|
|
44
|
+
const locales = options?.locales ?? DEFAULT_RESERVED_LOCALES;
|
|
45
|
+
let p = pathname || '/';
|
|
46
|
+
// Tolerate a full URL or a path carrying a query/hash.
|
|
47
|
+
if (/^[a-z]+:\/\//i.test(p)) {
|
|
48
|
+
try {
|
|
49
|
+
p = new URL(p).pathname;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// fall through with the raw value
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
const cut = p.search(/[?#]/);
|
|
57
|
+
if (cut >= 0)
|
|
58
|
+
p = p.slice(0, cut);
|
|
59
|
+
}
|
|
60
|
+
if (!p.startsWith('/'))
|
|
61
|
+
p = '/' + p;
|
|
62
|
+
// Normalize a trailing slash away (except for the bare root).
|
|
63
|
+
if (p.length > 1 && p.endsWith('/'))
|
|
64
|
+
p = p.replace(/\/+$/, '') || '/';
|
|
65
|
+
if (p === '/')
|
|
66
|
+
return true;
|
|
67
|
+
const firstSegment = p.split('/')[1];
|
|
68
|
+
if (locales.includes(firstSegment))
|
|
69
|
+
return true;
|
|
70
|
+
if (p === '/static' || p.startsWith('/static/'))
|
|
71
|
+
return true;
|
|
72
|
+
if (p === '/.well-known' || p.startsWith('/.well-known/'))
|
|
73
|
+
return true;
|
|
74
|
+
if (p === '/robots.txt')
|
|
75
|
+
return true;
|
|
76
|
+
if (/^\/sitemap[-\w]*\.xml$/.test(p))
|
|
77
|
+
return true;
|
|
78
|
+
if (p === '/favicon.ico' || p === '/favicon.svg')
|
|
79
|
+
return true;
|
|
80
|
+
if (p === '/manifest.webmanifest')
|
|
81
|
+
return true;
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=reserved-public-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reserved-public-path.js","sourceRoot":"","sources":["../../../../../libs/core/src/lib/reserved-public-path.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,+EAA+E;AAC/E,gFAAgF;AAChF,8BAA8B;AAC9B,EAAE;AACF,qEAAqE;AACrE,mEAAmE;AACnE,EAAE;AACF,gFAAgF;AAEhF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAsB,CAAC,IAAI,CAAC,CAAC;AAWlE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAgB,EAChB,OAAmC;IAEnC,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,wBAAwB,CAAC;IAE7D,IAAI,CAAC,GAAG,QAAQ,IAAI,GAAG,CAAC;IACxB,uDAAuD;IACvD,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,kCAAkC;QACpC,CAAC;IACH,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC7B,IAAI,GAAG,IAAI,CAAC;YAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpC,CAAC;IACD,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;IACpC,8DAA8D;IAC9D,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC;IAEtE,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAE3B,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,IAAI,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC;QAAE,OAAO,IAAI,CAAC;IAEhD,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7D,IAAI,CAAC,KAAK,cAAc,IAAI,CAAC,CAAC,UAAU,CAAC,eAAe,CAAC;QAAE,OAAO,IAAI,CAAC;IACvE,IAAI,CAAC,KAAK,aAAa;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAClD,IAAI,CAAC,KAAK,cAAc,IAAI,CAAC,KAAK,cAAc;QAAE,OAAO,IAAI,CAAC;IAC9D,IAAI,CAAC,KAAK,uBAAuB;QAAE,OAAO,IAAI,CAAC;IAE/C,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["// Reserved public paths for the standard Sneat routing model: the Angular\n// application is mounted at the ROOT (`/`), and a small, fixed set of paths is\n// reserved for the public landing/content surface. The edge (Cloudflare Worker)\n// uses this to split traffic:\n//\n// if (isReservedPublicPath(pathname)) return serveLanding(request)\n// return serveApplication(request) // root-mounted Angular SPA\n//\n// See docs/extension-standards/routing-and-deployment.md for the full contract.\n\n/**\n * Locale codes whose `/{locale}/*` subtree is reserved for landing/content\n * pages. Two-letter ISO-639-1 codes.\n *\n * This is an explicit ALLOW-LIST, not an \"any two-letter segment\" rule, and\n * that distinction is load-bearing: `/my` (the personal-dashboard section) is\n * also two letters. Matching \"any two letters\" would swallow `/my/assets` as if\n * `my` were a locale. With an allow-list, unknown two-letter segments fall\n * through to the application (fail-closed): `/my/assets` → app, `/en/about` →\n * landing.\n */\nexport const DEFAULT_RESERVED_LOCALES: readonly string[] = ['en'];\n\nexport interface ReservedPublicPathOptions {\n /**\n * Locale codes reserved for `/{locale}/*` landing content. Defaults to\n * {@link DEFAULT_RESERVED_LOCALES}. Pass the set your landing actually\n * publishes so unknown locales fail closed to the application.\n */\n locales?: readonly string[];\n}\n\n/**\n * Returns `true` when `pathname` belongs to the public landing/content surface\n * rather than the root-mounted Angular application.\n *\n * Reserved:\n * - `/` — root landing page\n * - `/{locale}` and `/{locale}/*` — localized content/marketing/legal/docs/SEO\n * (locale ∈ the allow-list; see {@link ReservedPublicPathOptions.locales})\n * - `/static` and `/static/*` — shared static files\n * - `/.well-known` and `/.well-known/*` — well-known files\n * - `/robots.txt`\n * - `/sitemap.xml`, `/sitemap-*.xml`, `/sitemap-index.xml`\n * - `/favicon.ico`, `/favicon.svg`\n * - `/manifest.webmanifest`\n *\n * Everything else (e.g. `/`, `/space/...`, `/my/assets`, `/event/abc`,\n * `/u/alex`) is an application route.\n *\n * Query strings and fragments are ignored — pass a bare pathname (a full URL is\n * tolerated and reduced to its pathname).\n */\nexport function isReservedPublicPath(\n pathname: string,\n options?: ReservedPublicPathOptions,\n): boolean {\n const locales = options?.locales ?? DEFAULT_RESERVED_LOCALES;\n\n let p = pathname || '/';\n // Tolerate a full URL or a path carrying a query/hash.\n if (/^[a-z]+:\\/\\//i.test(p)) {\n try {\n p = new URL(p).pathname;\n } catch {\n // fall through with the raw value\n }\n } else {\n const cut = p.search(/[?#]/);\n if (cut >= 0) p = p.slice(0, cut);\n }\n if (!p.startsWith('/')) p = '/' + p;\n // Normalize a trailing slash away (except for the bare root).\n if (p.length > 1 && p.endsWith('/')) p = p.replace(/\\/+$/, '') || '/';\n\n if (p === '/') return true;\n\n const firstSegment = p.split('/')[1];\n if (locales.includes(firstSegment)) return true;\n\n if (p === '/static' || p.startsWith('/static/')) return true;\n if (p === '/.well-known' || p.startsWith('/.well-known/')) return true;\n if (p === '/robots.txt') return true;\n if (/^\\/sitemap[-\\w]*\\.xml$/.test(p)) return true;\n if (p === '/favicon.ico' || p === '/favicon.svg') return true;\n if (p === '/manifest.webmanifest') return true;\n\n return false;\n}\n"]}
|
package/lib/index.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export * from './sneat-extensions';
|
|
|
14
14
|
export * from './form-field';
|
|
15
15
|
export * from './team-type';
|
|
16
16
|
export * from './location-href';
|
|
17
|
+
export * from './reserved-public-path';
|
|
17
18
|
export * from './interfaces';
|
|
18
19
|
export * from './current-space-storage';
|
|
19
20
|
export * from './directives';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locale codes whose `/{locale}/*` subtree is reserved for landing/content
|
|
3
|
+
* pages. Two-letter ISO-639-1 codes.
|
|
4
|
+
*
|
|
5
|
+
* This is an explicit ALLOW-LIST, not an "any two-letter segment" rule, and
|
|
6
|
+
* that distinction is load-bearing: `/my` (the personal-dashboard section) is
|
|
7
|
+
* also two letters. Matching "any two letters" would swallow `/my/assets` as if
|
|
8
|
+
* `my` were a locale. With an allow-list, unknown two-letter segments fall
|
|
9
|
+
* through to the application (fail-closed): `/my/assets` → app, `/en/about` →
|
|
10
|
+
* landing.
|
|
11
|
+
*/
|
|
12
|
+
export declare const DEFAULT_RESERVED_LOCALES: readonly string[];
|
|
13
|
+
export interface ReservedPublicPathOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Locale codes reserved for `/{locale}/*` landing content. Defaults to
|
|
16
|
+
* {@link DEFAULT_RESERVED_LOCALES}. Pass the set your landing actually
|
|
17
|
+
* publishes so unknown locales fail closed to the application.
|
|
18
|
+
*/
|
|
19
|
+
locales?: readonly string[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Returns `true` when `pathname` belongs to the public landing/content surface
|
|
23
|
+
* rather than the root-mounted Angular application.
|
|
24
|
+
*
|
|
25
|
+
* Reserved:
|
|
26
|
+
* - `/` — root landing page
|
|
27
|
+
* - `/{locale}` and `/{locale}/*` — localized content/marketing/legal/docs/SEO
|
|
28
|
+
* (locale ∈ the allow-list; see {@link ReservedPublicPathOptions.locales})
|
|
29
|
+
* - `/static` and `/static/*` — shared static files
|
|
30
|
+
* - `/.well-known` and `/.well-known/*` — well-known files
|
|
31
|
+
* - `/robots.txt`
|
|
32
|
+
* - `/sitemap.xml`, `/sitemap-*.xml`, `/sitemap-index.xml`
|
|
33
|
+
* - `/favicon.ico`, `/favicon.svg`
|
|
34
|
+
* - `/manifest.webmanifest`
|
|
35
|
+
*
|
|
36
|
+
* Everything else (e.g. `/`, `/space/...`, `/my/assets`, `/event/abc`,
|
|
37
|
+
* `/u/alex`) is an application route.
|
|
38
|
+
*
|
|
39
|
+
* Query strings and fragments are ignored — pass a bare pathname (a full URL is
|
|
40
|
+
* tolerated and reduced to its pathname).
|
|
41
|
+
*/
|
|
42
|
+
export declare function isReservedPublicPath(pathname: string, options?: ReservedPublicPathOptions): boolean;
|