solidstep 0.1.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/LICENSE +21 -0
- package/README.md +18 -0
- package/client.d.ts +4 -0
- package/client.d.ts.map +1 -0
- package/client.js +91 -0
- package/index.d.ts +11 -0
- package/index.d.ts.map +1 -0
- package/index.js +97 -0
- package/package.json +58 -0
- package/server.d.ts +3 -0
- package/server.d.ts.map +1 -0
- package/server.js +666 -0
- package/utils/cache.d.ts +5 -0
- package/utils/cache.d.ts.map +1 -0
- package/utils/cache.js +97 -0
- package/utils/cookies.d.ts +5 -0
- package/utils/cookies.d.ts.map +1 -0
- package/utils/cookies.js +13 -0
- package/utils/cors.d.ts +14 -0
- package/utils/cors.d.ts.map +1 -0
- package/utils/cors.js +15 -0
- package/utils/csp.d.ts +38 -0
- package/utils/csp.d.ts.map +1 -0
- package/utils/csp.js +165 -0
- package/utils/csrf.d.ts +5 -0
- package/utils/csrf.d.ts.map +1 -0
- package/utils/csrf.js +46 -0
- package/utils/error-handler.d.ts +37 -0
- package/utils/error-handler.d.ts.map +1 -0
- package/utils/error-handler.js +40 -0
- package/utils/fetch.client.d.ts +23 -0
- package/utils/fetch.client.d.ts.map +1 -0
- package/utils/fetch.client.js +55 -0
- package/utils/fetch.server.d.ts +22 -0
- package/utils/fetch.server.d.ts.map +1 -0
- package/utils/fetch.server.js +54 -0
- package/utils/hooks/action-state.d.ts +3 -0
- package/utils/hooks/action-state.d.ts.map +1 -0
- package/utils/hooks/action-state.js +13 -0
- package/utils/loader.d.ts +18 -0
- package/utils/loader.d.ts.map +1 -0
- package/utils/loader.js +23 -0
- package/utils/redirect.d.ts +5 -0
- package/utils/redirect.d.ts.map +1 -0
- package/utils/redirect.js +14 -0
- package/utils/router.d.ts +104 -0
- package/utils/router.d.ts.map +1 -0
- package/utils/router.js +258 -0
- package/utils/server-action.client.d.ts +2 -0
- package/utils/server-action.client.d.ts.map +1 -0
- package/utils/server-action.client.js +200 -0
- package/utils/server-action.server.d.ts +5 -0
- package/utils/server-action.server.d.ts.map +1 -0
- package/utils/server-action.server.js +264 -0
- package/utils/server-only.d.ts +2 -0
- package/utils/server-only.d.ts.map +1 -0
- package/utils/server-only.js +4 -0
- package/utils/types.d.ts +8 -0
- package/utils/types.d.ts.map +1 -0
- package/utils/types.js +1 -0
package/utils/cache.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const MAX_CACHE_ENTRIES = 1000;
|
|
2
|
+
const cacheMap = new Map();
|
|
3
|
+
let head;
|
|
4
|
+
let tail;
|
|
5
|
+
const moveToFront = (node) => {
|
|
6
|
+
if (node === head)
|
|
7
|
+
return;
|
|
8
|
+
// Detach
|
|
9
|
+
if (node.prev)
|
|
10
|
+
node.prev.next = node.next;
|
|
11
|
+
if (node.next)
|
|
12
|
+
node.next.prev = node.prev;
|
|
13
|
+
if (node === tail)
|
|
14
|
+
tail = node.prev;
|
|
15
|
+
// Insert at head
|
|
16
|
+
node.prev = undefined;
|
|
17
|
+
node.next = head;
|
|
18
|
+
if (head)
|
|
19
|
+
head.prev = node;
|
|
20
|
+
head = node;
|
|
21
|
+
if (!tail)
|
|
22
|
+
tail = node;
|
|
23
|
+
};
|
|
24
|
+
const removeTail = () => {
|
|
25
|
+
if (!tail)
|
|
26
|
+
return;
|
|
27
|
+
cacheMap.delete(tail.key);
|
|
28
|
+
if (tail.prev) {
|
|
29
|
+
tail.prev.next = undefined;
|
|
30
|
+
tail = tail.prev;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
// Only one node
|
|
34
|
+
head = tail = undefined;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
export const getCache = (key) => {
|
|
38
|
+
const entry = cacheMap.get(key);
|
|
39
|
+
if (!entry)
|
|
40
|
+
return null;
|
|
41
|
+
if (entry.expiresAt && entry.expiresAt < Date.now()) {
|
|
42
|
+
cacheMap.delete(key);
|
|
43
|
+
if (entry.prev)
|
|
44
|
+
entry.prev.next = entry.next;
|
|
45
|
+
if (entry.next)
|
|
46
|
+
entry.next.prev = entry.prev;
|
|
47
|
+
if (entry === head)
|
|
48
|
+
head = entry.next;
|
|
49
|
+
if (entry === tail)
|
|
50
|
+
tail = entry.prev;
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
moveToFront(entry);
|
|
54
|
+
return entry.value;
|
|
55
|
+
};
|
|
56
|
+
export const setCache = (key, value, ttlMs) => {
|
|
57
|
+
if (cacheMap.has(key)) {
|
|
58
|
+
const node = cacheMap.get(key);
|
|
59
|
+
node.value = value;
|
|
60
|
+
node.expiresAt = ttlMs ? Date.now() + ttlMs : null;
|
|
61
|
+
moveToFront(node);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const newNode = {
|
|
65
|
+
key,
|
|
66
|
+
value,
|
|
67
|
+
expiresAt: ttlMs ? Date.now() + ttlMs : null
|
|
68
|
+
};
|
|
69
|
+
newNode.next = head;
|
|
70
|
+
if (head)
|
|
71
|
+
head.prev = newNode;
|
|
72
|
+
head = newNode;
|
|
73
|
+
if (!tail)
|
|
74
|
+
tail = newNode;
|
|
75
|
+
cacheMap.set(key, newNode);
|
|
76
|
+
if (cacheMap.size > MAX_CACHE_ENTRIES) {
|
|
77
|
+
removeTail();
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
export const invalidateCache = (key) => {
|
|
81
|
+
const node = cacheMap.get(key);
|
|
82
|
+
if (!node)
|
|
83
|
+
return;
|
|
84
|
+
if (node.prev)
|
|
85
|
+
node.prev.next = node.next;
|
|
86
|
+
if (node.next)
|
|
87
|
+
node.next.prev = node.prev;
|
|
88
|
+
if (node === head)
|
|
89
|
+
head = node.next;
|
|
90
|
+
if (node === tail)
|
|
91
|
+
tail = node.prev;
|
|
92
|
+
cacheMap.delete(key);
|
|
93
|
+
};
|
|
94
|
+
export const clearAllCache = () => {
|
|
95
|
+
cacheMap.clear();
|
|
96
|
+
head = tail = undefined;
|
|
97
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { setCookie as baseSetCookie } from 'vinxi/http';
|
|
2
|
+
export declare const getCookie: (key: string) => string | undefined;
|
|
3
|
+
export declare const setCookie: (key: string, value: string, options?: Parameters<typeof baseSetCookie>[2]) => void;
|
|
4
|
+
export declare const deleteCookie: (key: string) => void;
|
|
5
|
+
//# sourceMappingURL=cookies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../utils/cookies.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,SAAS,IAAI,aAAa,EAI7B,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,SAAS,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,SAGhD,CAAC;AAEF,eAAO,MAAM,SAAS,GAClB,KAAK,MAAM,EACX,OAAO,MAAM,EACb,UAAU,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC,CAAC,CAAC,SAIhD,CAAA;AAED,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,SAGvC,CAAC"}
|
package/utils/cookies.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { setCookie as baseSetCookie, getCookie as baseGetCookie, deleteCookie as baseDeleteCookie, getEvent } from 'vinxi/http';
|
|
2
|
+
export const getCookie = (key) => {
|
|
3
|
+
const event = getEvent();
|
|
4
|
+
return baseGetCookie(event, key);
|
|
5
|
+
};
|
|
6
|
+
export const setCookie = (key, value, options) => {
|
|
7
|
+
const event = getEvent();
|
|
8
|
+
return baseSetCookie(event, key, value, options);
|
|
9
|
+
};
|
|
10
|
+
export const deleteCookie = (key) => {
|
|
11
|
+
const event = getEvent();
|
|
12
|
+
return baseDeleteCookie(event, key);
|
|
13
|
+
};
|
package/utils/cors.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const cors: (trustedOrigins: string[]) => (origin: string, isPreflight: boolean) => {
|
|
2
|
+
'Access-Control-Allow-Origin': string;
|
|
3
|
+
'Access-Control-Allow-Methods': string;
|
|
4
|
+
'Access-Control-Allow-Headers': string;
|
|
5
|
+
} | {
|
|
6
|
+
'Access-Control-Allow-Origin': string;
|
|
7
|
+
'Access-Control-Allow-Methods'?: undefined;
|
|
8
|
+
'Access-Control-Allow-Headers'?: undefined;
|
|
9
|
+
} | {
|
|
10
|
+
'Access-Control-Allow-Origin'?: undefined;
|
|
11
|
+
'Access-Control-Allow-Methods'?: undefined;
|
|
12
|
+
'Access-Control-Allow-Headers'?: undefined;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=cors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../utils/cors.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,IAAI,GAAI,gBAAgB,MAAM,EAAE,MAAM,QAAQ,MAAM,EAAE,aAAa,OAAO;;;;;;;;;;;;CActF,CAAA"}
|
package/utils/cors.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const cors = (trustedOrigins) => (origin, isPreflight) => {
|
|
2
|
+
if (trustedOrigins.includes(origin)) {
|
|
3
|
+
if (isPreflight) {
|
|
4
|
+
return {
|
|
5
|
+
'Access-Control-Allow-Origin': origin,
|
|
6
|
+
'Access-Control-Allow-Methods': 'GET, POST, PUT, PATCH, DELETE, OPTIONS',
|
|
7
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
'Access-Control-Allow-Origin': origin,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
return {};
|
|
15
|
+
};
|
package/utils/csp.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content Security Policy Builder
|
|
3
|
+
* Functional, extensible, and type-safe CSP construction
|
|
4
|
+
*/
|
|
5
|
+
type DirectiveName = 'default-src' | 'script-src' | 'style-src' | 'style-src-elem' | 'img-src' | 'font-src' | 'connect-src' | 'media-src' | 'object-src' | 'frame-src' | 'frame-ancestors' | 'base-uri' | 'form-action' | 'worker-src' | 'manifest-src';
|
|
6
|
+
type Source = "'self'" | "'none'" | "'unsafe-inline'" | "'unsafe-eval'" | "'unsafe-hashes'" | "'wasm-unsafe-eval'" | "'strict-dynamic'" | 'data:' | 'blob:' | 'filesystem:' | 'ws:' | 'wss:' | 'https:' | 'http:' | string;
|
|
7
|
+
type CSPDirective = {
|
|
8
|
+
readonly name: DirectiveName;
|
|
9
|
+
readonly sources: ReadonlyArray<Source>;
|
|
10
|
+
};
|
|
11
|
+
type CSPPolicy = ReadonlyArray<CSPDirective>;
|
|
12
|
+
declare const createDirective: (name: DirectiveName, sources: Source[]) => CSPDirective;
|
|
13
|
+
declare const addSource: (directive: CSPDirective, source: Source) => CSPDirective;
|
|
14
|
+
declare const removeSource: (directive: CSPDirective, source: Source) => CSPDirective;
|
|
15
|
+
declare const setSources: (directive: CSPDirective, sources: Source[]) => CSPDirective;
|
|
16
|
+
declare const addDirective: (policy: CSPPolicy, directive: CSPDirective) => CSPPolicy;
|
|
17
|
+
declare const updateDirective: (policy: CSPPolicy, name: DirectiveName, updater: (directive: CSPDirective) => CSPDirective) => CSPPolicy;
|
|
18
|
+
declare const removeDirective: (policy: CSPPolicy, name: DirectiveName) => CSPPolicy;
|
|
19
|
+
declare const getDirective: (policy: CSPPolicy, name: DirectiveName) => CSPDirective | undefined;
|
|
20
|
+
declare const mergePolicies: (...policies: CSPPolicy[]) => CSPPolicy;
|
|
21
|
+
declare const serializeDirective: (directive: CSPDirective) => string;
|
|
22
|
+
declare const serializePolicy: (policy: CSPPolicy) => string;
|
|
23
|
+
declare const parseDirective: (directiveStr: string) => CSPDirective | null;
|
|
24
|
+
declare const parsePolicy: (policyStr: string) => CSPPolicy;
|
|
25
|
+
declare const createStrictPolicy: () => CSPPolicy;
|
|
26
|
+
declare const createBasePolicy: () => CSPPolicy;
|
|
27
|
+
declare const withDevelopmentSources: (policy: CSPPolicy) => CSPPolicy;
|
|
28
|
+
declare const withProductionSources: (policy: CSPPolicy) => CSPPolicy;
|
|
29
|
+
declare const withCDN: (policy: CSPPolicy, cdnUrl: string) => CSPPolicy;
|
|
30
|
+
declare const withGoogleFonts: (policy: CSPPolicy) => CSPPolicy;
|
|
31
|
+
declare const withWebSockets: (policy: CSPPolicy, secure?: boolean) => CSPPolicy;
|
|
32
|
+
declare const withNonce: (policy: CSPPolicy, nonce: string, directives?: DirectiveName[]) => CSPPolicy;
|
|
33
|
+
declare const withHash: (policy: CSPPolicy, hash: string, algorithm?: "sha256" | "sha384" | "sha512", directive?: DirectiveName) => CSPPolicy;
|
|
34
|
+
declare const isDirectivePresent: (policy: CSPPolicy, name: DirectiveName) => boolean;
|
|
35
|
+
declare const hasSource: (policy: CSPPolicy, name: DirectiveName, source: Source) => boolean;
|
|
36
|
+
declare const reportUnsafeDirectives: (policy: CSPPolicy) => DirectiveName[];
|
|
37
|
+
export { type DirectiveName, type Source, type CSPDirective, type CSPPolicy, createDirective, addSource, removeSource, setSources, addDirective, updateDirective, removeDirective, getDirective, mergePolicies, serializePolicy, serializeDirective, parsePolicy, parseDirective, createStrictPolicy, createBasePolicy, withDevelopmentSources, withProductionSources, withCDN, withGoogleFonts, withWebSockets, withNonce, withHash, isDirectivePresent, hasSource, reportUnsafeDirectives, };
|
|
38
|
+
//# sourceMappingURL=csp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csp.d.ts","sourceRoot":"","sources":["../../utils/csp.ts"],"names":[],"mappings":"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"}
|
package/utils/csp.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content Security Policy Builder
|
|
3
|
+
* Functional, extensible, and type-safe CSP construction
|
|
4
|
+
*/
|
|
5
|
+
// Core Builder Functions
|
|
6
|
+
const createDirective = (name, sources) => ({
|
|
7
|
+
name,
|
|
8
|
+
sources,
|
|
9
|
+
});
|
|
10
|
+
const addSource = (directive, source) => ({
|
|
11
|
+
...directive,
|
|
12
|
+
sources: [...directive.sources, source],
|
|
13
|
+
});
|
|
14
|
+
const removeSource = (directive, source) => ({
|
|
15
|
+
...directive,
|
|
16
|
+
sources: directive.sources.filter((s) => s !== source),
|
|
17
|
+
});
|
|
18
|
+
const setSources = (directive, sources) => ({
|
|
19
|
+
...directive,
|
|
20
|
+
sources,
|
|
21
|
+
});
|
|
22
|
+
// Policy Manipulation
|
|
23
|
+
const addDirective = (policy, directive) => {
|
|
24
|
+
const existing = policy.find((d) => d.name === directive.name);
|
|
25
|
+
if (existing) {
|
|
26
|
+
return policy.map((d) => d.name === directive.name ? directive : d);
|
|
27
|
+
}
|
|
28
|
+
return [...policy, directive];
|
|
29
|
+
};
|
|
30
|
+
const updateDirective = (policy, name, updater) => {
|
|
31
|
+
const existing = policy.find((d) => d.name === name);
|
|
32
|
+
if (!existing) {
|
|
33
|
+
return policy;
|
|
34
|
+
}
|
|
35
|
+
return policy.map((d) => (d.name === name ? updater(d) : d));
|
|
36
|
+
};
|
|
37
|
+
const removeDirective = (policy, name) => policy.filter((d) => d.name !== name);
|
|
38
|
+
const getDirective = (policy, name) => policy.find((d) => d.name === name);
|
|
39
|
+
// Merging Policies
|
|
40
|
+
const mergeDirectives = (directive1, directive2) => {
|
|
41
|
+
const uniqueSources = Array.from(new Set([...directive1.sources, ...directive2.sources]));
|
|
42
|
+
return createDirective(directive1.name, uniqueSources);
|
|
43
|
+
};
|
|
44
|
+
const mergePolicies = (...policies) => {
|
|
45
|
+
const directiveMap = new Map();
|
|
46
|
+
for (const policy of policies) {
|
|
47
|
+
for (const directive of policy) {
|
|
48
|
+
const existing = directiveMap.get(directive.name);
|
|
49
|
+
if (existing) {
|
|
50
|
+
directiveMap.set(directive.name, mergeDirectives(existing, directive));
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
directiveMap.set(directive.name, directive);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return Array.from(directiveMap.values());
|
|
58
|
+
};
|
|
59
|
+
// String Conversion
|
|
60
|
+
const serializeDirective = (directive) => `${directive.name} ${directive.sources.join(' ')}`;
|
|
61
|
+
const serializePolicy = (policy) => policy.map(serializeDirective).join('; ');
|
|
62
|
+
const parseDirective = (directiveStr) => {
|
|
63
|
+
const parts = directiveStr.trim().split(/\s+/);
|
|
64
|
+
if (parts.length < 1)
|
|
65
|
+
return null;
|
|
66
|
+
const [name, ...sources] = parts;
|
|
67
|
+
return createDirective(name, sources);
|
|
68
|
+
};
|
|
69
|
+
const parsePolicy = (policyStr) => {
|
|
70
|
+
const directives = policyStr
|
|
71
|
+
.split(';')
|
|
72
|
+
.map((d) => d.trim())
|
|
73
|
+
.filter((d) => d.length > 0)
|
|
74
|
+
.map(parseDirective)
|
|
75
|
+
.filter((d) => d !== null);
|
|
76
|
+
return directives;
|
|
77
|
+
};
|
|
78
|
+
// Preset Builders
|
|
79
|
+
const createStrictPolicy = () => [
|
|
80
|
+
createDirective('default-src', ["'self'"]),
|
|
81
|
+
createDirective('object-src', ["'none'"]),
|
|
82
|
+
createDirective('base-uri', ["'none'"]),
|
|
83
|
+
createDirective('frame-ancestors', ["'none'"]),
|
|
84
|
+
createDirective('form-action', ["'self'"]),
|
|
85
|
+
];
|
|
86
|
+
const createBasePolicy = () => [
|
|
87
|
+
createDirective('default-src', ["'self'"]),
|
|
88
|
+
createDirective('font-src', ["'self'", 'https://fonts.gstatic.com']),
|
|
89
|
+
createDirective('object-src', ["'none'"]),
|
|
90
|
+
createDirective('base-uri', ["'none'"]),
|
|
91
|
+
createDirective('frame-ancestors', ["'none'"]),
|
|
92
|
+
createDirective('form-action', ["'self'"]),
|
|
93
|
+
createDirective('style-src', ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com']),
|
|
94
|
+
createDirective('style-src-elem', ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com']),
|
|
95
|
+
createDirective('script-src', ["'self'", "'unsafe-inline'", "'unsafe-eval'"]),
|
|
96
|
+
createDirective('connect-src', ["'self'", 'ws:']),
|
|
97
|
+
createDirective('img-src', ["'self'", 'data:']),
|
|
98
|
+
];
|
|
99
|
+
// Environment-Specific Builders
|
|
100
|
+
const withDevelopmentSources = (policy) => {
|
|
101
|
+
const devPolicy = [
|
|
102
|
+
createDirective('script-src', ["'unsafe-eval'", "'unsafe-inline'"]),
|
|
103
|
+
createDirective('connect-src', ['ws:', 'wss:']),
|
|
104
|
+
];
|
|
105
|
+
return mergePolicies(policy, devPolicy);
|
|
106
|
+
};
|
|
107
|
+
const withProductionSources = (policy) => updateDirective(policy, 'script-src', (d) => removeSource(removeSource(d, "'unsafe-eval'"), "'unsafe-inline'"));
|
|
108
|
+
// Common Integrations
|
|
109
|
+
const withCDN = (policy, cdnUrl) => {
|
|
110
|
+
const cdnPolicy = [
|
|
111
|
+
createDirective('script-src', [cdnUrl]),
|
|
112
|
+
createDirective('style-src', [cdnUrl]),
|
|
113
|
+
createDirective('img-src', [cdnUrl]),
|
|
114
|
+
createDirective('font-src', [cdnUrl]),
|
|
115
|
+
];
|
|
116
|
+
return mergePolicies(policy, cdnPolicy);
|
|
117
|
+
};
|
|
118
|
+
const withGoogleFonts = (policy) => {
|
|
119
|
+
const googleFontsPolicy = [
|
|
120
|
+
createDirective('font-src', ['https://fonts.gstatic.com']),
|
|
121
|
+
createDirective('style-src', ['https://fonts.googleapis.com']),
|
|
122
|
+
];
|
|
123
|
+
return mergePolicies(policy, googleFontsPolicy);
|
|
124
|
+
};
|
|
125
|
+
const withWebSockets = (policy, secure = false) => updateDirective(policy, 'connect-src', (d) => addSource(d, secure ? 'wss:' : 'ws:'));
|
|
126
|
+
const withNonce = (policy, nonce, directives = ['script-src', 'style-src']) => {
|
|
127
|
+
const nonceSource = `'nonce-${nonce}'`;
|
|
128
|
+
let updatedPolicy = policy;
|
|
129
|
+
for (const directive of directives) {
|
|
130
|
+
updatedPolicy = updateDirective(updatedPolicy, directive, (d) => addSource(d, nonceSource));
|
|
131
|
+
}
|
|
132
|
+
return updatedPolicy;
|
|
133
|
+
};
|
|
134
|
+
const withHash = (policy, hash, algorithm = 'sha256', directive = 'script-src') => {
|
|
135
|
+
const hashSource = `'${algorithm}-${hash}'`;
|
|
136
|
+
return updateDirective(policy, directive, (d) => addSource(d, hashSource));
|
|
137
|
+
};
|
|
138
|
+
// Utility Functions
|
|
139
|
+
const isDirectivePresent = (policy, name) => policy.some((d) => d.name === name);
|
|
140
|
+
const hasSource = (policy, name, source) => {
|
|
141
|
+
const directive = getDirective(policy, name);
|
|
142
|
+
return directive ? directive.sources.includes(source) : false;
|
|
143
|
+
};
|
|
144
|
+
const reportUnsafeDirectives = (policy) => {
|
|
145
|
+
const unsafeSources = ["'unsafe-inline'", "'unsafe-eval'"];
|
|
146
|
+
return policy
|
|
147
|
+
.filter((d) => d.sources.some((s) => unsafeSources.includes(s)))
|
|
148
|
+
.map((d) => d.name);
|
|
149
|
+
};
|
|
150
|
+
// Export
|
|
151
|
+
export {
|
|
152
|
+
// Core
|
|
153
|
+
createDirective, addSource, removeSource, setSources,
|
|
154
|
+
// Policy
|
|
155
|
+
addDirective, updateDirective, removeDirective, getDirective, mergePolicies,
|
|
156
|
+
// Serialization
|
|
157
|
+
serializePolicy, serializeDirective, parsePolicy, parseDirective,
|
|
158
|
+
// Presets
|
|
159
|
+
createStrictPolicy, createBasePolicy,
|
|
160
|
+
// Environment
|
|
161
|
+
withDevelopmentSources, withProductionSources,
|
|
162
|
+
// Integrations
|
|
163
|
+
withCDN, withGoogleFonts, withWebSockets, withNonce, withHash,
|
|
164
|
+
// Utils
|
|
165
|
+
isDirectivePresent, hasSource, reportUnsafeDirectives, };
|
package/utils/csrf.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csrf.d.ts","sourceRoot":"","sources":["../../utils/csrf.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,IAAI,GAAI,gBAAgB,MAAM,EAAE,MAErC,eAAe,MAAM,EACrB,YAAY,GAAG,EACf,SAAS,MAAM,EACf,UAAU,MAAM;;;CAsDnB,CAAC"}
|
package/utils/csrf.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const SAFE_METHODS = ['GET', 'OPTIONS', 'HEAD', 'TRACE'];
|
|
2
|
+
export const csrf = (trustedOrigins) => (requestMethod, requestUrl, origin, referer) => {
|
|
3
|
+
// Check if the request method is safe
|
|
4
|
+
if (!SAFE_METHODS.includes(requestMethod)) {
|
|
5
|
+
// If we have an Origin header, check it against our allowlist.
|
|
6
|
+
if (origin) {
|
|
7
|
+
const parsedOrigin = new URL(origin);
|
|
8
|
+
if (parsedOrigin.origin !== requestUrl.origin &&
|
|
9
|
+
!trustedOrigins.includes(parsedOrigin.host)) {
|
|
10
|
+
return {
|
|
11
|
+
success: false,
|
|
12
|
+
message: 'Invalid origin',
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// If we are serving via TLS and have no Origin header, prevent against
|
|
17
|
+
// CSRF via HTTP man-in-the-middle attacks by enforcing strict Referer
|
|
18
|
+
// origin checks.
|
|
19
|
+
if (!origin && requestUrl.protocol === 'https:') {
|
|
20
|
+
if (!referer) {
|
|
21
|
+
return {
|
|
22
|
+
success: false,
|
|
23
|
+
message: 'referer not supplied',
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const parsedReferer = new URL(referer);
|
|
27
|
+
if (parsedReferer.protocol !== 'https:') {
|
|
28
|
+
return {
|
|
29
|
+
success: false,
|
|
30
|
+
message: 'Invalid referer',
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (parsedReferer.host !== requestUrl.host &&
|
|
34
|
+
!trustedOrigins.includes(parsedReferer.host)) {
|
|
35
|
+
return {
|
|
36
|
+
success: false,
|
|
37
|
+
message: 'Invalid referer',
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
success: true,
|
|
44
|
+
message: 'CSRF check passed',
|
|
45
|
+
};
|
|
46
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type ErrorSeverity = 'low' | 'medium' | 'high' | 'critical';
|
|
2
|
+
export type AppError<Code extends string = string> = {
|
|
3
|
+
code: Code;
|
|
4
|
+
message: string;
|
|
5
|
+
severity: ErrorSeverity;
|
|
6
|
+
metadata?: Record<string, unknown>;
|
|
7
|
+
cause?: unknown;
|
|
8
|
+
action: (error?: AppError<Code>) => void | Promise<void>;
|
|
9
|
+
};
|
|
10
|
+
export type ErrorDefinition<Code extends string> = {
|
|
11
|
+
message: string;
|
|
12
|
+
severity: ErrorSeverity;
|
|
13
|
+
defaultMetadata?: Record<string, unknown>;
|
|
14
|
+
action?: (error: AppError<Code>) => void | Promise<void>;
|
|
15
|
+
};
|
|
16
|
+
export type ErrorCatalog<Code extends string = string> = {
|
|
17
|
+
[K in Code]: ErrorDefinition<K>;
|
|
18
|
+
};
|
|
19
|
+
type CreateErrorOptions<Code extends string> = Partial<Pick<AppError<Code>, 'message' | 'severity' | 'metadata' | 'cause' | 'action'>>;
|
|
20
|
+
export declare class FunctionalAppError<Code extends string = string> extends Error implements AppError<Code> {
|
|
21
|
+
readonly code: Code;
|
|
22
|
+
readonly severity: ErrorSeverity;
|
|
23
|
+
readonly metadata?: Record<string, unknown>;
|
|
24
|
+
readonly cause?: unknown;
|
|
25
|
+
readonly action: () => void;
|
|
26
|
+
constructor(params: {
|
|
27
|
+
code: Code;
|
|
28
|
+
message: string;
|
|
29
|
+
severity: ErrorSeverity;
|
|
30
|
+
metadata?: Record<string, unknown>;
|
|
31
|
+
cause?: unknown;
|
|
32
|
+
action: (error: AppError<Code>) => void;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export declare const createErrorFactory: <Code extends string>(catalog: ErrorCatalog<Code>) => (code: Code, overrides?: CreateErrorOptions<Code>) => FunctionalAppError<Code>;
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=error-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../utils/error-handler.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAEnE,MAAM,MAAM,QAAQ,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,IAAI;IACjD,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,aAAa,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D,CAAC;AAEF,MAAM,MAAM,eAAe,CAAC,IAAI,SAAS,MAAM,IAAI;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,aAAa,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D,CAAC;AAEF,MAAM,MAAM,YAAY,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,IAAI;KACpD,CAAC,IAAI,IAAI,GAAG,eAAe,CAAC,CAAC,CAAC;CAClC,CAAC;AAEF,KAAK,kBAAkB,CAAC,IAAI,SAAS,MAAM,IAAI,OAAO,CAClD,IAAI,CACA,QAAQ,CAAC,IAAI,CAAC,EACd,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,OAAO,GAAG,QAAQ,CAC3D,CACJ,CAAC;AAEF,qBAAa,kBAAkB,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,CACxD,SAAQ,KACR,YAAW,QAAQ,CAAC,IAAI,CAAC;IAEzB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC;gBAEhB,MAAM,EAAE;QAChB,IAAI,EAAE,IAAI,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,aAAa,CAAC;QACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACnC,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,MAAM,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;KAC3C;CAcJ;AAED,eAAO,MAAM,kBAAkB,GAAI,IAAI,SAAS,MAAM,EAClD,SAAS,YAAY,CAAC,IAAI,CAAC,MAGvB,MAAM,IAAI,EACV,YAAW,kBAAkB,CAAC,IAAI,CAAM,KACzC,kBAAkB,CAAC,IAAI,CAwB7B,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export class FunctionalAppError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
severity;
|
|
4
|
+
metadata;
|
|
5
|
+
cause;
|
|
6
|
+
action;
|
|
7
|
+
constructor(params) {
|
|
8
|
+
super(params.message);
|
|
9
|
+
this.name = 'AppError';
|
|
10
|
+
this.code = params.code;
|
|
11
|
+
this.severity = params.severity;
|
|
12
|
+
this.metadata = params.metadata;
|
|
13
|
+
this.cause = params.cause;
|
|
14
|
+
// 👇 wrap action to pass this instance
|
|
15
|
+
this.action = () => params.action(this);
|
|
16
|
+
// ✅ Restore native error prototype chain (important for `instanceof`)
|
|
17
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export const createErrorFactory = (catalog) => {
|
|
21
|
+
return (code, overrides = {}) => {
|
|
22
|
+
const def = catalog[code];
|
|
23
|
+
const actionFn = overrides.action ??
|
|
24
|
+
def.action ??
|
|
25
|
+
((error) => {
|
|
26
|
+
console.error(`[Unhandled Error]: ${error.code} - ${error.message}`);
|
|
27
|
+
});
|
|
28
|
+
return new FunctionalAppError({
|
|
29
|
+
code,
|
|
30
|
+
message: overrides.message ?? def.message,
|
|
31
|
+
severity: overrides.severity ?? def.severity,
|
|
32
|
+
metadata: {
|
|
33
|
+
...def.defaultMetadata,
|
|
34
|
+
...overrides.metadata,
|
|
35
|
+
},
|
|
36
|
+
cause: overrides.cause,
|
|
37
|
+
action: actionFn,
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type FetchResponse<T, S extends boolean> = S extends true ? T : Response;
|
|
2
|
+
type Options = {
|
|
3
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
4
|
+
body?: any;
|
|
5
|
+
headers?: any;
|
|
6
|
+
MAX_FETCH_TIME?: number;
|
|
7
|
+
serverAction?: boolean;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* It's a wrapper around the native fetch function that adds a timeout and a json parser
|
|
11
|
+
* @param {string} url - string - The URL to fetch
|
|
12
|
+
* @param {Options} [options] - {
|
|
13
|
+
* method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
14
|
+
* body?: any;
|
|
15
|
+
* headers?: any;
|
|
16
|
+
* MAX_FETCH_TIME?: number;
|
|
17
|
+
* }
|
|
18
|
+
* @param [json=true] - boolean - If the response is JSON, it will be parsed and returned.
|
|
19
|
+
* @returns The return type is Promise<any>
|
|
20
|
+
*/
|
|
21
|
+
declare const Fetch: <T, S extends boolean = true>(url: string, options?: Options, json?: S) => Promise<FetchResponse<T, S>>;
|
|
22
|
+
export default Fetch;
|
|
23
|
+
//# sourceMappingURL=fetch.client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.client.d.ts","sourceRoot":"","sources":["../../utils/fetch.client.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,CAAC,CAAC,EAAE,CAAC,SAAS,OAAO,IAAI,CAAC,SAAS,IAAI,GAAG,CAAC,GAAG,QAAQ,CAAC;AAEhF,KAAK,OAAO,GAAG;IACX,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpD,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,QAAA,MAAM,KAAK,GAAU,CAAC,EAAE,CAAC,SAAS,OAAO,GAAG,IAAI,EAC5C,KAAK,MAAM,EACX,UAAU,OAAO,EACjB,OAAM,CAAa,KACpB,OAAO,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAgD7B,CAAC;AAEF,eAAe,KAAK,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* It's a wrapper around the native fetch function that adds a timeout and a json parser
|
|
3
|
+
* @param {string} url - string - The URL to fetch
|
|
4
|
+
* @param {Options} [options] - {
|
|
5
|
+
* method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
6
|
+
* body?: any;
|
|
7
|
+
* headers?: any;
|
|
8
|
+
* MAX_FETCH_TIME?: number;
|
|
9
|
+
* }
|
|
10
|
+
* @param [json=true] - boolean - If the response is JSON, it will be parsed and returned.
|
|
11
|
+
* @returns The return type is Promise<any>
|
|
12
|
+
*/
|
|
13
|
+
const Fetch = async (url, options, json = true) => {
|
|
14
|
+
const maxTime = options?.MAX_FETCH_TIME ?? 4000;
|
|
15
|
+
const controller = new AbortController();
|
|
16
|
+
const timeout = setTimeout(() => {
|
|
17
|
+
controller.abort();
|
|
18
|
+
}, maxTime);
|
|
19
|
+
try {
|
|
20
|
+
const response = await fetch(url, {
|
|
21
|
+
...options,
|
|
22
|
+
signal: controller.signal,
|
|
23
|
+
});
|
|
24
|
+
if (response?.status >= 400
|
|
25
|
+
&& response?.status <= 599) {
|
|
26
|
+
if (json) {
|
|
27
|
+
throw await response.json();
|
|
28
|
+
}
|
|
29
|
+
throw response;
|
|
30
|
+
}
|
|
31
|
+
if (json) {
|
|
32
|
+
const data = await response.json();
|
|
33
|
+
if (data) {
|
|
34
|
+
if (data.error)
|
|
35
|
+
throw data.error; // in case the status is marked as 200 but the response is an error
|
|
36
|
+
return data;
|
|
37
|
+
}
|
|
38
|
+
throw new Error('Not Defined');
|
|
39
|
+
}
|
|
40
|
+
return response;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (controller.signal.aborted) {
|
|
44
|
+
throw new Error('Timeout');
|
|
45
|
+
}
|
|
46
|
+
if (options?.serverAction) {
|
|
47
|
+
return error;
|
|
48
|
+
}
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
clearTimeout(timeout);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
export default Fetch;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type FetchResponse<T, S extends boolean> = S extends true ? T : Response;
|
|
2
|
+
type Options = {
|
|
3
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
4
|
+
body?: any;
|
|
5
|
+
headers?: any;
|
|
6
|
+
MAX_FETCH_TIME?: number;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* It's a wrapper around the native fetch function that adds a timeout and a json parser
|
|
10
|
+
* @param {string} url - string - The URL to fetch
|
|
11
|
+
* @param {Options} [options] - {
|
|
12
|
+
* method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
13
|
+
* body?: any;
|
|
14
|
+
* headers?: any;
|
|
15
|
+
* MAX_FETCH_TIME?: number;
|
|
16
|
+
* }
|
|
17
|
+
* @param [json=true] - boolean - If the response is JSON, it will be parsed and returned.
|
|
18
|
+
* @returns The return type is Promise<any>
|
|
19
|
+
*/
|
|
20
|
+
declare const Fetch: <T, S extends boolean = true>(url: string, options?: Options, json?: S) => Promise<FetchResponse<T, S>>;
|
|
21
|
+
export default Fetch;
|
|
22
|
+
//# sourceMappingURL=fetch.server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.server.d.ts","sourceRoot":"","sources":["../../utils/fetch.server.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,aAAa,CAAC,CAAC,EAAE,CAAC,SAAS,OAAO,IAAI,CAAC,SAAS,IAAI,GAC1D,CAAC,GACD,QAAQ,CAAC;AAEf,KAAK,OAAO,GAAG;IACX,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpD,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,QAAA,MAAM,KAAK,GAAU,CAAC,EAAE,CAAC,SAAS,OAAO,GAAG,IAAI,EAC5C,KAAK,MAAM,EACX,UAAU,OAAO,EACjB,OAAM,CAAa,KACpB,OAAO,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CA4C7B,CAAC;AAEF,eAAe,KAAK,CAAC"}
|