@cripty2001/utils 0.0.244 → 0.0.245
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/dist/Appserver/client.d.ts +1 -41
- package/dist/Appserver/client.js +145 -125
- package/dist/Appserver/server.d.ts +37 -27
- package/dist/Appserver/server.js +165 -102
- package/dist/react-ui/loader.d.ts +1 -0
- package/dist/react-ui/loader.js +13 -1
- package/package.json +4 -11
- package/llms/appserver.md +0 -571
- package/llms/index.md +0 -1
package/dist/Appserver/server.js
CHANGED
|
@@ -3,49 +3,148 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
const promises_1 = require("fs/promises");
|
|
6
|
+
exports.AppserverUserHandledError = exports.AppserverAuthHandledError = exports.Appserver = exports.Type = void 0;
|
|
7
|
+
const fastify_1 = __importDefault(require("fastify"));
|
|
8
|
+
const type_provider_typebox_1 = require("@fastify/type-provider-typebox");
|
|
9
|
+
Object.defineProperty(exports, "Type", { enumerable: true, get: function () { return type_provider_typebox_1.Type; } });
|
|
11
10
|
const path_1 = __importDefault(require("path"));
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
11
|
+
const promises_1 = require("fs/promises");
|
|
12
|
+
const jose_1 = require("jose");
|
|
13
|
+
class Appserver {
|
|
14
|
+
fastify = (0, fastify_1.default)({ logger: true })
|
|
15
|
+
.withTypeProvider();
|
|
16
|
+
jwks;
|
|
17
|
+
issuer;
|
|
18
|
+
audience;
|
|
19
|
+
static async resolveAuth(baseUrl) {
|
|
20
|
+
const res = await fetch(`${baseUrl}/.well-known/openid-configuration`);
|
|
21
|
+
if (!res.ok)
|
|
22
|
+
throw new Error(`OIDC discovery failed: ${res.status}`);
|
|
23
|
+
const config = await res.json();
|
|
24
|
+
if (!config?.jwks_uri)
|
|
25
|
+
throw new Error('OIDC discovery failed: jwks_uri not found');
|
|
26
|
+
if (!config.issuer)
|
|
27
|
+
throw new Error('OIDC discovery failed: issuer not found');
|
|
28
|
+
const jwksUrl = config.jwks_uri;
|
|
29
|
+
const issuer = config.issuer;
|
|
30
|
+
const jwks = (0, jose_1.createRemoteJWKSet)(new URL(jwksUrl));
|
|
31
|
+
return {
|
|
32
|
+
jwks,
|
|
33
|
+
issuer
|
|
34
|
+
};
|
|
29
35
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
rpcBase;
|
|
37
|
+
constructor(rpcBase, jwks, issuer, audience) {
|
|
38
|
+
this.rpcBase = rpcBase;
|
|
39
|
+
this.jwks = jwks;
|
|
40
|
+
this.issuer = issuer;
|
|
41
|
+
this.audience = audience;
|
|
34
42
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
static async create(audience, issuerUrl, rpcBase = '/exec') {
|
|
44
|
+
const { jwks, issuer } = await this.resolveAuth(issuerUrl);
|
|
45
|
+
const appserver = new Appserver(rpcBase, jwks, issuer, audience);
|
|
46
|
+
appserver.fastify.setErrorHandler(appserver.handleError
|
|
47
|
+
.bind(appserver));
|
|
48
|
+
appserver.fastify.addContentTypeParser('application/octet-stream', { parseAs: 'buffer', bodyLimit: 64 * 1024 * 1024 }, (_req, body, done) => {
|
|
49
|
+
done(null, body);
|
|
50
|
+
});
|
|
51
|
+
appserver.fastify.decorateRequest('user', null);
|
|
52
|
+
return appserver;
|
|
40
53
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
54
|
+
async listen(port) {
|
|
55
|
+
await this.fastify.listen({
|
|
56
|
+
port,
|
|
57
|
+
host: '0.0.0.0'
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
registerRpc(route, schema, scope, handler) {
|
|
61
|
+
const routeSchema = {
|
|
62
|
+
body: schema.input,
|
|
63
|
+
response: {
|
|
64
|
+
200: schema.output,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
this.fastify.post(`${this.rpcBase}/${route.replace(/^\/+/, '')}`, {
|
|
68
|
+
preHandler: [
|
|
69
|
+
this.enforceAccess(scope)
|
|
70
|
+
],
|
|
71
|
+
schema: routeSchema,
|
|
72
|
+
}, async (request) => {
|
|
73
|
+
const user = request.user;
|
|
74
|
+
return handler(request.body, user);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
enforceAccess(scope) {
|
|
78
|
+
return async (request, _reply) => {
|
|
79
|
+
const token = request.headers.authorization;
|
|
80
|
+
if (!token || !token.startsWith('Bearer '))
|
|
81
|
+
throw new AppserverAuthHandledError('HEADER_MISSING', 'Missing or invalid authorization header', true);
|
|
82
|
+
const payload = await (async () => {
|
|
83
|
+
try {
|
|
84
|
+
const { payload } = await (0, jose_1.jwtVerify)(token.slice(7), this.jwks, {
|
|
85
|
+
issuer: this.issuer,
|
|
86
|
+
audience: this.audience,
|
|
87
|
+
requiredClaims: ['sub'],
|
|
88
|
+
});
|
|
89
|
+
return payload;
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
if (err instanceof jose_1.errors.JWTExpired)
|
|
93
|
+
throw new AppserverAuthHandledError('TOKEN_EXPIRED', 'Access token has expired.', true);
|
|
94
|
+
if (err instanceof jose_1.errors.JWTInvalid
|
|
95
|
+
|| err instanceof jose_1.errors.JWTClaimValidationFailed
|
|
96
|
+
|| err instanceof jose_1.errors.JWSSignatureVerificationFailed
|
|
97
|
+
|| err instanceof jose_1.errors.JWSInvalid
|
|
98
|
+
|| err instanceof jose_1.errors.JWKSNoMatchingKey
|
|
99
|
+
|| err instanceof jose_1.errors.JOSEAlgNotAllowed)
|
|
100
|
+
throw new AppserverAuthHandledError('TOKEN_INVALID', 'Access token is invalid', true);
|
|
101
|
+
if (err instanceof jose_1.errors.JWKSTimeout || err instanceof jose_1.errors.JWKSInvalid)
|
|
102
|
+
throw new Error('Failed to verify the access token against the identity provider JWKS.', { cause: err });
|
|
103
|
+
if (err instanceof Error)
|
|
104
|
+
throw err;
|
|
105
|
+
throw new Error('Unexpected failure while verifying the access token.', {
|
|
106
|
+
cause: err
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
})();
|
|
110
|
+
const scopes = payload.scope?.split(' ') ?? [];
|
|
111
|
+
if (!scopes.includes(`res:${scope}`))
|
|
112
|
+
throw new AppserverAuthHandledError('MISSING_SCOPE', 'Access token scope is not valid.', false);
|
|
113
|
+
if (!payload.sub)
|
|
114
|
+
throw new Error('JOSE assert failed, sub is missing despite being required');
|
|
115
|
+
request.user = {
|
|
116
|
+
id: payload.sub
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
handleError(error, request, reply) {
|
|
121
|
+
if (error.validation)
|
|
122
|
+
return reply
|
|
123
|
+
.code(422)
|
|
124
|
+
.send({
|
|
125
|
+
errors: error.validation,
|
|
126
|
+
});
|
|
127
|
+
if (error instanceof AppserverHandledError)
|
|
128
|
+
return reply
|
|
129
|
+
.status(error.status)
|
|
130
|
+
.send({
|
|
131
|
+
code: error.code,
|
|
132
|
+
message: error.message,
|
|
133
|
+
details: error.details,
|
|
134
|
+
});
|
|
135
|
+
request.log.error({
|
|
136
|
+
error: {
|
|
137
|
+
name: error.name,
|
|
138
|
+
message: error.message,
|
|
139
|
+
stack: error.stack,
|
|
140
|
+
}
|
|
141
|
+
}, 'unhandled error');
|
|
142
|
+
reply
|
|
143
|
+
.status(500)
|
|
144
|
+
.send({
|
|
145
|
+
code: 'INTERNAL_ERROR',
|
|
146
|
+
message: 'Internal server error',
|
|
147
|
+
});
|
|
49
148
|
}
|
|
50
149
|
async autoload(base) {
|
|
51
150
|
const absBase = path_1.default.resolve(base);
|
|
@@ -53,7 +152,8 @@ class AppserverDispatcher {
|
|
|
53
152
|
recursive: true,
|
|
54
153
|
withFileTypes: true
|
|
55
154
|
});
|
|
56
|
-
const jsFiles = files
|
|
155
|
+
const jsFiles = files
|
|
156
|
+
.filter(e => e.isFile() && e.name.endsWith('.js'));
|
|
57
157
|
if (jsFiles.length === 0) {
|
|
58
158
|
console.warn(`[appserver] autoload found no .js files in "${absBase}".`, `If you are using TypeScript, make sure the api directory is included in your tsconfig`, `and that you are pointing autoload at the compiled output directory, not the source.`);
|
|
59
159
|
return;
|
|
@@ -69,70 +169,33 @@ class AppserverDispatcher {
|
|
|
69
169
|
if (!ACTION_REGEX.test(action))
|
|
70
170
|
throw new Error(`Invalid module path "${action}" — all segments must be lowercase alphanumeric`);
|
|
71
171
|
const mod = await import(fullPath);
|
|
72
|
-
if (!mod.default
|
|
73
|
-
throw new Error(`Module "${fullPath}" must have a default export
|
|
74
|
-
|
|
172
|
+
if (!mod.default)
|
|
173
|
+
throw new Error(`Module "${fullPath}" must have a default export containing a registering function (register(app: Appserver) => void)`);
|
|
174
|
+
mod.default(this, action);
|
|
75
175
|
}
|
|
76
176
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
177
|
+
}
|
|
178
|
+
exports.Appserver = Appserver;
|
|
179
|
+
class AppserverHandledError extends Error {
|
|
180
|
+
status;
|
|
181
|
+
code;
|
|
182
|
+
details;
|
|
183
|
+
constructor(code, message, status, details) {
|
|
184
|
+
super(message);
|
|
185
|
+
this.status = status;
|
|
186
|
+
this.code = code;
|
|
187
|
+
this.details = details;
|
|
84
188
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
try {
|
|
96
|
-
return (0, msgpack_1.decode)(req.body);
|
|
97
|
-
}
|
|
98
|
-
catch (e) {
|
|
99
|
-
throw new AppserverError('REQUEST_INVALID_BODY', 'Request body is not valid msgpack', {}, 400);
|
|
100
|
-
}
|
|
101
|
-
})();
|
|
102
|
-
const { action, payload } = parsed;
|
|
103
|
-
if (typeof action !== 'string')
|
|
104
|
-
throw new AppserverError("REQUEST_INVALID_ACTION", "Missing action field", {}, 400);
|
|
105
|
-
const mod = this.registry.get(action);
|
|
106
|
-
if (mod === undefined)
|
|
107
|
-
throw new AppserverError("ACTION_NOT_FOUND", `Action not found: ${action}`, {}, 404);
|
|
108
|
-
const token = req.headers['authorization']?.startsWith('Bearer ') ?
|
|
109
|
-
req.headers['authorization'].slice(7) :
|
|
110
|
-
undefined;
|
|
111
|
-
if (!value_1.Value.Check(mod.schema, payload))
|
|
112
|
-
return respond(422, {
|
|
113
|
-
errors: [...value_1.Value.Errors(mod.schema, payload)]
|
|
114
|
-
.map(e => ({
|
|
115
|
-
path: e.path,
|
|
116
|
-
message: e.message,
|
|
117
|
-
value: e.value,
|
|
118
|
-
})),
|
|
119
|
-
received: payload,
|
|
120
|
-
});
|
|
121
|
-
return respond(200, await mod.handler(payload, token));
|
|
122
|
-
}
|
|
123
|
-
catch (e) {
|
|
124
|
-
if (e instanceof AppserverError)
|
|
125
|
-
return respond(e.status, {
|
|
126
|
-
error: e.message,
|
|
127
|
-
code: e.code,
|
|
128
|
-
payload: e.payload
|
|
129
|
-
});
|
|
130
|
-
console.error("Unhandled server error:", e);
|
|
131
|
-
return respond(500, {
|
|
132
|
-
error: 'Internal server error',
|
|
133
|
-
code: 'INTERNAL_SERVERERROR'
|
|
134
|
-
});
|
|
135
|
-
}
|
|
189
|
+
}
|
|
190
|
+
class AppserverAuthHandledError extends AppserverHandledError {
|
|
191
|
+
constructor(code, message, recoverable = false) {
|
|
192
|
+
super(code, message, recoverable ? 401 : 403, {});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
exports.AppserverAuthHandledError = AppserverAuthHandledError;
|
|
196
|
+
class AppserverUserHandledError extends AppserverHandledError {
|
|
197
|
+
constructor(code, message, details) {
|
|
198
|
+
super(code, message, 400, details ?? {});
|
|
136
199
|
}
|
|
137
200
|
}
|
|
138
|
-
exports.
|
|
201
|
+
exports.AppserverUserHandledError = AppserverUserHandledError;
|
package/dist/react-ui/loader.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.default = Loader;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const react_whispr_1 = require("../react-whispr");
|
|
6
|
+
const react_1 = require("react");
|
|
6
7
|
const spinKeyframes = `
|
|
7
8
|
@keyframes spin {
|
|
8
9
|
from { transform: rotate(0deg); }
|
|
@@ -18,7 +19,18 @@ if (typeof document !== 'undefined') {
|
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
21
|
function Loader(props) {
|
|
21
|
-
const
|
|
22
|
+
const prev = (0, react_1.useRef)({ loading: true, progress: 0 });
|
|
23
|
+
const state = (0, react_whispr_1.useWhisprValue)(props.data.data);
|
|
24
|
+
(0, react_1.useEffect)(() => {
|
|
25
|
+
prev.current = state;
|
|
26
|
+
}, [state]);
|
|
27
|
+
const data = (0, react_1.useMemo)(() => {
|
|
28
|
+
if (props.showRefreshing)
|
|
29
|
+
return state;
|
|
30
|
+
if (state.loading && prev.current !== null)
|
|
31
|
+
return prev.current;
|
|
32
|
+
return state;
|
|
33
|
+
}, [state.loading, prev.current, props.showRefreshing]);
|
|
22
34
|
return ((0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)(Content, { data: data, children: props.children, loader: props.loader }) }));
|
|
23
35
|
}
|
|
24
36
|
function Content({ data, children, loader }) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cripty2001/utils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.245",
|
|
4
4
|
"description": "Internal Set of utils. If you need them use them, otherwise go to the next package ;)",
|
|
5
5
|
"homepage": "https://github.com/cripty2001/utils#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -31,12 +31,9 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@cripty2001/whispr": "^0.4.1",
|
|
33
33
|
"@sinclair/typebox": "^0.34.41",
|
|
34
|
-
"cors": "^2.8.6",
|
|
35
34
|
"lodash": "^4.17.21"
|
|
36
35
|
},
|
|
37
36
|
"devDependencies": {
|
|
38
|
-
"@types/cors": "^2.8.19",
|
|
39
|
-
"@types/express": "^5.0.5",
|
|
40
37
|
"@types/lodash": "^4.17.21",
|
|
41
38
|
"@types/node": "^24.13.3",
|
|
42
39
|
"@types/react": "^19",
|
|
@@ -44,9 +41,10 @@
|
|
|
44
41
|
"typescript": "^7.0.2"
|
|
45
42
|
},
|
|
46
43
|
"peerDependencies": {
|
|
47
|
-
"@
|
|
44
|
+
"@fastify/type-provider-typebox": "^6.1.0",
|
|
48
45
|
"dotenv": "^17.2.3",
|
|
49
|
-
"
|
|
46
|
+
"fastify": "^5.12.5",
|
|
47
|
+
"jose": "^6.2.12",
|
|
50
48
|
"lucide-react": ">=0.552.0",
|
|
51
49
|
"react": "^19"
|
|
52
50
|
},
|
|
@@ -96,11 +94,6 @@
|
|
|
96
94
|
"types": "./dist/Appserver/server.d.ts",
|
|
97
95
|
"require": "./dist/Appserver/server.js"
|
|
98
96
|
},
|
|
99
|
-
"./appserver/client": {
|
|
100
|
-
"import": "./dist/Appserver/client.js",
|
|
101
|
-
"types": "./dist/Appserver/client.d.ts",
|
|
102
|
-
"require": "./dist/Appserver/client.js"
|
|
103
|
-
},
|
|
104
97
|
"./react": {
|
|
105
98
|
"import": "./dist/react/index.js",
|
|
106
99
|
"types": "./dist/react/index.d.ts",
|