@divizend/scratch-core 1.0.0 → 1.0.2
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/basic/index.ts +6 -1
- package/core/ProjectRoot.ts +9 -0
- package/core/Universe.ts +20 -27
- package/http-server/ExpressHttpServer.ts +1175 -0
- package/http-server/HttpServer.ts +13 -10
- package/http-server/default-endpoints/index.ts +4 -0
- package/http-server/default-endpoints/loadEndpoints.ts +69 -0
- package/http-server/default-endpoints/loadEndpointsUI.ts +43 -0
- package/http-server/index.ts +1 -1
- package/index.ts +43 -0
- package/package.json +3 -1
- package/s2/index.ts +19 -0
- package/http-server/NativeHttpServer.ts +0 -1084
- package/http-server/middlewares/01-cors.ts +0 -33
- package/http-server/middlewares/02-static.ts +0 -67
- package/http-server/middlewares/03-request-logger.ts +0 -159
- package/http-server/middlewares/04-body-parser.ts +0 -54
- package/http-server/middlewares/05-no-cache.ts +0 -23
- package/http-server/middlewares/06-response-handler.ts +0 -39
- package/http-server/middlewares/handler-wrapper.ts +0 -250
- package/http-server/middlewares/index.ts +0 -37
- package/http-server/middlewares/types.ts +0 -27
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* HttpServer - Abstract HTTP server interface
|
|
3
3
|
*
|
|
4
4
|
* This interface defines the contract for HTTP server implementations.
|
|
5
|
-
* Currently, only
|
|
5
|
+
* Currently, only ExpressHttpServer is implemented using Express.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { ScratchEndpointDefinition, ScratchContext } from "../index";
|
|
@@ -21,12 +21,6 @@ export interface HttpServer {
|
|
|
21
21
|
*/
|
|
22
22
|
stop(): Promise<void>;
|
|
23
23
|
|
|
24
|
-
/**
|
|
25
|
-
* Get the fetch handler for the server (for Bun/Cloudflare Workers)
|
|
26
|
-
* @returns Fetch handler function
|
|
27
|
-
*/
|
|
28
|
-
getFetchHandler(): (request: Request) => Promise<Response>;
|
|
29
|
-
|
|
30
24
|
/**
|
|
31
25
|
* Register static file serving
|
|
32
26
|
* @param rootPath - Root path for static files
|
|
@@ -34,10 +28,13 @@ export interface HttpServer {
|
|
|
34
28
|
registerStaticFiles(rootPath: string): void;
|
|
35
29
|
|
|
36
30
|
/**
|
|
37
|
-
* Load endpoints from a
|
|
38
|
-
* @param
|
|
31
|
+
* Load endpoints from a URL
|
|
32
|
+
* @param url - URL to load endpoints from (file:// or https://github.com)
|
|
33
|
+
* @returns Statistics about loaded and failed endpoints
|
|
39
34
|
*/
|
|
40
|
-
|
|
35
|
+
loadEndpointsFromUrl(
|
|
36
|
+
url: string
|
|
37
|
+
): Promise<{ loaded: number; failed: number; errors: string[] }>;
|
|
41
38
|
|
|
42
39
|
/**
|
|
43
40
|
* Get all endpoint definitions
|
|
@@ -85,6 +82,12 @@ export interface HttpServer {
|
|
|
85
82
|
| undefined
|
|
86
83
|
>;
|
|
87
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Register the catch-all 404 handler
|
|
87
|
+
* This must be called AFTER all endpoints are loaded to ensure it's registered last
|
|
88
|
+
*/
|
|
89
|
+
register404Handler(): void;
|
|
90
|
+
|
|
88
91
|
/**
|
|
89
92
|
* Register an endpoint from TypeScript source code (PUT operation - always overwrites)
|
|
90
93
|
* @param source - TypeScript source code for the endpoint
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ScratchEndpointDefinition } from "../../index";
|
|
2
|
+
|
|
3
|
+
export const loadEndpoints: ScratchEndpointDefinition = {
|
|
4
|
+
block: async () => ({
|
|
5
|
+
opcode: "loadEndpoints",
|
|
6
|
+
blockType: "command",
|
|
7
|
+
text: "load endpoints from URL [path] [jwt]",
|
|
8
|
+
schema: {
|
|
9
|
+
path: {
|
|
10
|
+
type: "string",
|
|
11
|
+
description:
|
|
12
|
+
"URL to load endpoints from (GitHub URL like https://github.com/owner/repo/tree/branch/path or file:// URL like file:///path/to/endpoints)",
|
|
13
|
+
},
|
|
14
|
+
jwt: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description:
|
|
17
|
+
"JWT token for authentication (will be used for subsequent requests)",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
}),
|
|
21
|
+
handler: async (context) => {
|
|
22
|
+
const { path } = context.inputs!;
|
|
23
|
+
if (!path || typeof path !== "string") {
|
|
24
|
+
throw new Error("path parameter is required and must be a string");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const endpointCountBefore =
|
|
28
|
+
context.universe!.httpServer.getAllEndpoints().length;
|
|
29
|
+
const result = await context.universe!.httpServer.loadEndpointsFromUrl(
|
|
30
|
+
path
|
|
31
|
+
);
|
|
32
|
+
const endpointCountAfter =
|
|
33
|
+
context.universe!.httpServer.getAllEndpoints().length;
|
|
34
|
+
const newEndpointsLoaded = endpointCountAfter - endpointCountBefore;
|
|
35
|
+
|
|
36
|
+
if (newEndpointsLoaded === 0 && result.failed > 0) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Failed to load any endpoints from ${path}. ${
|
|
39
|
+
result.failed
|
|
40
|
+
} file(s) failed to load. Errors: ${result.errors
|
|
41
|
+
.slice(0, 5)
|
|
42
|
+
.join("; ")}${
|
|
43
|
+
result.errors.length > 5
|
|
44
|
+
? ` (and ${result.errors.length - 5} more)`
|
|
45
|
+
: ""
|
|
46
|
+
}`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (result.failed > 0) {
|
|
51
|
+
return {
|
|
52
|
+
success: true,
|
|
53
|
+
message: `Loaded ${newEndpointsLoaded} endpoint(s) from ${path} (${result.failed} file(s) failed)`,
|
|
54
|
+
totalEndpoints: endpointCountAfter,
|
|
55
|
+
loaded: result.loaded,
|
|
56
|
+
failed: result.failed,
|
|
57
|
+
errors: result.errors.slice(0, 10),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
success: true,
|
|
63
|
+
message: `Successfully loaded ${newEndpointsLoaded} endpoint(s) from ${path}`,
|
|
64
|
+
totalEndpoints: endpointCountAfter,
|
|
65
|
+
loaded: result.loaded,
|
|
66
|
+
failed: result.failed,
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ScratchEndpointDefinition } from "../../index";
|
|
2
|
+
|
|
3
|
+
export const loadEndpointsUI: ScratchEndpointDefinition = {
|
|
4
|
+
block: async () => ({
|
|
5
|
+
opcode: "loadEndpointsUI",
|
|
6
|
+
blockType: "reporter",
|
|
7
|
+
text: "load endpoints UI [jwt]",
|
|
8
|
+
schema: {
|
|
9
|
+
jwt: {
|
|
10
|
+
type: "string",
|
|
11
|
+
description: "JWT token for authentication",
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
}),
|
|
15
|
+
handler: async (context) => {
|
|
16
|
+
const { jwt } = context.inputs!;
|
|
17
|
+
const html = `<!DOCTYPE html>
|
|
18
|
+
<html>
|
|
19
|
+
<head>
|
|
20
|
+
<title>Load Endpoints</title>
|
|
21
|
+
<style>
|
|
22
|
+
body { font-family: system-ui, -apple-system, sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; }
|
|
23
|
+
input[type="text"] { width: 100%; padding: 10px; font-size: 16px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }
|
|
24
|
+
button { margin-top: 10px; padding: 10px 20px; font-size: 16px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; }
|
|
25
|
+
button:hover { background: #0056b3; }
|
|
26
|
+
label { display: block; margin-bottom: 5px; font-weight: 500; }
|
|
27
|
+
</style>
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
<h1>Load Endpoints</h1>
|
|
31
|
+
<p>No endpoints loaded yet. Enter a URL to load endpoints from:</p>
|
|
32
|
+
<form method="POST" action="/loadEndpoints">
|
|
33
|
+
<input type="hidden" name="jwt" value="${jwt || ""}">
|
|
34
|
+
<label for="path">Endpoint URL:</label>
|
|
35
|
+
<input type="text" id="path" name="path" placeholder="file:///path/to/endpoints or https://github.com/owner/repo/tree/branch/path" required>
|
|
36
|
+
<button type="submit">Load Endpoints</button>
|
|
37
|
+
</form>
|
|
38
|
+
</body>
|
|
39
|
+
</html>`;
|
|
40
|
+
return html;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
package/http-server/index.ts
CHANGED
package/index.ts
CHANGED
|
@@ -15,10 +15,53 @@
|
|
|
15
15
|
* @author Divizend GmbH
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
console.log("[@divizend/scratch-core] Loading package/index.ts");
|
|
19
|
+
console.log("[@divizend/scratch-core] import.meta.url:", import.meta.url);
|
|
20
|
+
console.log(
|
|
21
|
+
"[@divizend/scratch-core] import.meta.dirname:",
|
|
22
|
+
import.meta.dirname
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
console.log("[@divizend/scratch-core] Exporting from ./basic");
|
|
18
26
|
export * from "./basic";
|
|
27
|
+
|
|
28
|
+
console.log("[@divizend/scratch-core] Exporting from ./core");
|
|
19
29
|
export * from "./core";
|
|
30
|
+
|
|
31
|
+
console.log("[@divizend/scratch-core] Exporting from ./gsuite");
|
|
20
32
|
export * from "./gsuite";
|
|
33
|
+
|
|
34
|
+
console.log("[@divizend/scratch-core] Exporting from ./http-server");
|
|
21
35
|
export * from "./http-server";
|
|
36
|
+
|
|
37
|
+
console.log("[@divizend/scratch-core] Exporting from ./resend");
|
|
22
38
|
export * from "./resend";
|
|
39
|
+
|
|
40
|
+
console.log("[@divizend/scratch-core] Exporting from ./s2");
|
|
23
41
|
export * from "./s2";
|
|
42
|
+
|
|
43
|
+
console.log("[@divizend/scratch-core] Exporting from ./queue");
|
|
24
44
|
export * from "./queue";
|
|
45
|
+
|
|
46
|
+
// After exports, check what's actually exported
|
|
47
|
+
(async () => {
|
|
48
|
+
try {
|
|
49
|
+
const s2Module = await import("./s2");
|
|
50
|
+
console.log(
|
|
51
|
+
"[@divizend/scratch-core] s2 module keys:",
|
|
52
|
+
Object.keys(s2Module)
|
|
53
|
+
);
|
|
54
|
+
console.log(
|
|
55
|
+
"[@divizend/scratch-core] S2 class in s2 module:",
|
|
56
|
+
"S2" in s2Module
|
|
57
|
+
);
|
|
58
|
+
console.log(
|
|
59
|
+
"[@divizend/scratch-core] S2ReadResult in s2 module:",
|
|
60
|
+
"S2ReadResult" in s2Module
|
|
61
|
+
);
|
|
62
|
+
} catch (e) {
|
|
63
|
+
console.error("[@divizend/scratch-core] Error checking s2 module:", e);
|
|
64
|
+
}
|
|
65
|
+
})();
|
|
66
|
+
|
|
67
|
+
console.log("[@divizend/scratch-core] Finished loading package/index.ts");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@divizend/scratch-core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Core library for Scratch endpoint system",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@s2-dev/streamstore": "^0.18.1",
|
|
18
18
|
"cloudevents": "^10.0.0",
|
|
19
|
+
"express": "^5.2.1",
|
|
19
20
|
"google-auth-library": "^10.5.0",
|
|
20
21
|
"googleapis": "^167.0.0",
|
|
21
22
|
"jose": "^5.6.0",
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
28
29
|
"@types/bun": "^1.3.3",
|
|
30
|
+
"@types/express": "^5.0.0",
|
|
29
31
|
"@types/mustache": "^4.2.6",
|
|
30
32
|
"@types/node": "^24.10.1",
|
|
31
33
|
"@types/turndown": "^5.0.6",
|
package/s2/index.ts
CHANGED
|
@@ -8,4 +8,23 @@
|
|
|
8
8
|
* @author Divizend GmbH
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
console.log("[@divizend/scratch-core/s2] Loading s2/index.ts");
|
|
12
|
+
console.log("[@divizend/scratch-core/s2] import.meta.url:", import.meta.url);
|
|
13
|
+
|
|
14
|
+
console.log("[@divizend/scratch-core/s2] Exporting from ./S2");
|
|
11
15
|
export * from "./S2";
|
|
16
|
+
|
|
17
|
+
// After export, check what's actually exported
|
|
18
|
+
(async () => {
|
|
19
|
+
try {
|
|
20
|
+
console.log("[@divizend/scratch-core/s2] Importing from ./S2 to verify");
|
|
21
|
+
const s2File = await import("./S2");
|
|
22
|
+
console.log("[@divizend/scratch-core/s2] S2.ts module keys:", Object.keys(s2File));
|
|
23
|
+
console.log("[@divizend/scratch-core/s2] S2 class exists:", "S2" in s2File);
|
|
24
|
+
console.log("[@divizend/scratch-core/s2] S2ReadResult exists:", "S2ReadResult" in s2File);
|
|
25
|
+
console.log("[@divizend/scratch-core/s2] Successfully verified ./S2 exports");
|
|
26
|
+
} catch (e) {
|
|
27
|
+
console.error("[@divizend/scratch-core/s2] Error verifying ./S2:", e);
|
|
28
|
+
console.error("[@divizend/scratch-core/s2] Error stack:", e instanceof Error ? e.stack : String(e));
|
|
29
|
+
}
|
|
30
|
+
})();
|