@primate/go 0.1.5 → 0.2.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.
@@ -0,0 +1,7 @@
1
+ import Runtime from "#Runtime";
2
+ import type BuildApp from "@primate/core/BuildApp";
3
+ import type NextBuild from "@primate/core/NextBuild";
4
+ export default class Default extends Runtime {
5
+ build(app: BuildApp, next: NextBuild): import("@rcompat/type/MaybePromise").default<BuildApp>;
6
+ }
7
+ //# sourceMappingURL=Default.d.ts.map
@@ -0,0 +1,115 @@
1
+ import Runtime from "#Runtime";
2
+ import AppError from "@primate/core/AppError";
3
+ import log from "@primate/core/log";
4
+ import wrap from "@primate/core/route/wrap";
5
+ import assert from "@rcompat/assert";
6
+ import user from "@rcompat/env/user";
7
+ import runtime from "@rcompat/runtime";
8
+ import execute from "@rcompat/stdio/execute";
9
+ import which from "@rcompat/stdio/which";
10
+ const command = await which("go");
11
+ const env = {
12
+ GOARCH: "wasm",
13
+ GOCACHE: (await execute(`${command} env GOCACHE`, {})).replaceAll("\n", ""),
14
+ GOOS: "js",
15
+ HOME: user.HOME,
16
+ };
17
+ const run = (wasm, go) => `${command} build -o ${wasm.name} ${go.name}`;
18
+ const js_wrapper = (path) => `
19
+ import env from "@primate/go/env";
20
+ import toRequest from "@primate/go/to-request";
21
+ import to_response from "@primate/go/to-response";
22
+ import session from "#session";
23
+ import route from "primate/route";
24
+
25
+ globalThis.PRMT_SESSION = {
26
+ get exists() {
27
+ return session.exists;
28
+ },
29
+ get id() {
30
+ return session.id;
31
+ },
32
+ get data() {
33
+ return JSON.stringify(session.try());
34
+ },
35
+ create(data) {
36
+ session.create(JSON.parse(data));
37
+ },
38
+ get() {
39
+ return JSON.stringify(session.get());
40
+ },
41
+ try() {
42
+ return JSON.stringify(session.try());
43
+ },
44
+ set(data) {
45
+ session.set(JSON.parse(data));
46
+ },
47
+ destroy() {
48
+ session.destroy();
49
+ },
50
+ };
51
+
52
+ ${runtime === "bun"
53
+ ? `import route_path from "${path}" with { type: "file" };
54
+ const go_route = await Bun.file(route_path).arrayBuffer();`
55
+ :
56
+ `import FileRef from "primate/runtime/FileRef";
57
+
58
+ const buffer = await FileRef.arrayBuffer(import.meta.url+"/../${path}");
59
+ const go_route = new Uint8Array(buffer);`}
60
+
61
+ env();
62
+
63
+ // Run Go once to register routes and get available verbs
64
+ const go = new globalThis.Go();
65
+ const result = await WebAssembly.instantiate(go_route, go.importObject);
66
+ go.run(result.instance);
67
+
68
+ const verbs = globalThis.__primate_verbs || [];
69
+
70
+ for (const verb of verbs) {
71
+ route[verb.toLowerCase()](async request => {
72
+ const requested = await toRequest(request);
73
+ const freshGo = new globalThis.Go();
74
+ return WebAssembly.instantiate(go_route, {...freshGo.importObject}).then(freshResult => {
75
+ freshGo.run(freshResult.instance);
76
+ return to_response(globalThis.__primate_handle(verb, requested));
77
+ });
78
+ });
79
+ }
80
+ `;
81
+ const go_wrapper = (code) => {
82
+ if (!code.includes("github.com/primate-run/go/route")) {
83
+ console.warn("Go file does not import \"github.com/primate-run/go/route\" - skipping route registration");
84
+ return code;
85
+ }
86
+ return `${code}
87
+
88
+ func main() {
89
+ route.Commit()
90
+ select{}
91
+ }`;
92
+ };
93
+ export default class Default extends Runtime {
94
+ build(app, next) {
95
+ app.bind(this.fileExtension, async (route, { build, context }) => {
96
+ assert(context === "routes", "go: only route files are supported");
97
+ const code = await route.text();
98
+ // wrap user code with main function if they imported our route package
99
+ await route.write(go_wrapper(code));
100
+ const wasm = route.bare(".wasm");
101
+ const js_code = wrap(js_wrapper(wasm.name), route, build);
102
+ await route.append(".js").write(js_code);
103
+ try {
104
+ log.info("compiling {0} to WebAssembly", route);
105
+ // compile .go to .wasm using user's go.mod from project root
106
+ await execute(run(wasm, route), { cwd: `${route.directory}`, env });
107
+ }
108
+ catch (error) {
109
+ throw new AppError("Error in module {0}\n{1}", route, error);
110
+ }
111
+ });
112
+ return next(app);
113
+ }
114
+ }
115
+ //# sourceMappingURL=Default.js.map
@@ -0,0 +1,6 @@
1
+ import Module from "@primate/core/backend/Module";
2
+ export default class Runtime extends Module {
3
+ name: string;
4
+ defaultExtension: string;
5
+ }
6
+ //# sourceMappingURL=Runtime.d.ts.map
@@ -0,0 +1,6 @@
1
+ import Module from "@primate/core/backend/Module";
2
+ export default class Runtime extends Module {
3
+ name = "go";
4
+ defaultExtension = ".go";
5
+ }
6
+ //# sourceMappingURL=Runtime.js.map
@@ -0,0 +1,45 @@
1
+ import type RequestFacade from "@primate/core/request/RequestFacade";
2
+ export default function toRequest(request: RequestFacade): Promise<{
3
+ body: {
4
+ fieldsSync: () => string;
5
+ filesSync: () => {
6
+ bytes: Uint8Array;
7
+ field: string;
8
+ name: string;
9
+ size: number;
10
+ type: string;
11
+ }[];
12
+ type: "fields";
13
+ } | {
14
+ textSync: () => string;
15
+ type: "text";
16
+ jsonSync?: undefined;
17
+ binarySync?: undefined;
18
+ binaryTypeSync?: undefined;
19
+ } | {
20
+ jsonSync: () => string;
21
+ type: "json";
22
+ textSync?: undefined;
23
+ binarySync?: undefined;
24
+ binaryTypeSync?: undefined;
25
+ } | {
26
+ binarySync: () => Uint8Array<ArrayBuffer>;
27
+ binaryTypeSync: () => string;
28
+ type: "binary";
29
+ textSync?: undefined;
30
+ jsonSync?: undefined;
31
+ } | {
32
+ type: "none";
33
+ textSync?: undefined;
34
+ jsonSync?: undefined;
35
+ binarySync?: undefined;
36
+ binaryTypeSync?: undefined;
37
+ };
38
+ cookies: string;
39
+ headers: string;
40
+ path: string;
41
+ query: string;
42
+ searchParams: string;
43
+ url: URL;
44
+ }>;
45
+ //# sourceMappingURL=to-request.d.ts.map
@@ -0,0 +1,76 @@
1
+ async function bridgeFields(body) {
2
+ const fields = body.fields();
3
+ const meta = Object.create(null);
4
+ const files = [];
5
+ const pending = [];
6
+ for (const [k, v] of Object.entries(fields)) {
7
+ if (typeof v === "string") {
8
+ meta[k] = v;
9
+ continue;
10
+ }
11
+ // v is File
12
+ const name = v.name;
13
+ const type = v.type;
14
+ const size = v.size;
15
+ meta[k] = { name, size, type };
16
+ // Precompute bytes so Go can call a SYNC getter
17
+ pending.push(v.arrayBuffer().then(buffer => {
18
+ files.push({
19
+ bytes: new Uint8Array(buffer),
20
+ field: k,
21
+ name,
22
+ size,
23
+ type,
24
+ });
25
+ }));
26
+ }
27
+ await Promise.all(pending);
28
+ const jsonStr = JSON.stringify(meta);
29
+ return {
30
+ fieldsSync: () => jsonStr,
31
+ filesSync: () => files,
32
+ type: "fields",
33
+ };
34
+ }
35
+ async function bridgeBody(body) {
36
+ const type = body.type;
37
+ switch (type) {
38
+ case "text": {
39
+ const s = body.text();
40
+ return { textSync: () => s, type };
41
+ }
42
+ case "json": {
43
+ const val = body.json();
44
+ const jsonStr = JSON.stringify(val);
45
+ return { jsonSync: () => jsonStr, type };
46
+ }
47
+ case "fields": {
48
+ return await bridgeFields(body);
49
+ }
50
+ case "binary": {
51
+ const blob = body.binary();
52
+ const buf = new Uint8Array(await blob.arrayBuffer());
53
+ const mime = blob.type || "application/octet-stream";
54
+ return {
55
+ binarySync: () => buf,
56
+ binaryTypeSync: () => mime,
57
+ type,
58
+ };
59
+ }
60
+ default:
61
+ return { type: "none" };
62
+ }
63
+ }
64
+ export default async function toRequest(request) {
65
+ const body = await bridgeBody(request.body);
66
+ return {
67
+ body,
68
+ cookies: JSON.stringify(request.cookies.toJSON()),
69
+ headers: JSON.stringify(request.headers.toJSON()),
70
+ path: JSON.stringify(request.path.toJSON()),
71
+ query: JSON.stringify(request.query.toJSON()),
72
+ searchParams: JSON.stringify(request.url.searchParams),
73
+ url: request.url,
74
+ };
75
+ }
76
+ //# sourceMappingURL=to-request.js.map
@@ -0,0 +1,9 @@
1
+ import type ResponseLike from "@primate/core/response/ResponseLike";
2
+ import type Dict from "@rcompat/type/Dict";
3
+ type Handler = "error" | "redirect" | "view";
4
+ type ResponseType = (() => {
5
+ handler: Handler;
6
+ } & Dict) | string;
7
+ declare const _default: (response: ResponseType) => ResponseLike;
8
+ export default _default;
9
+ //# sourceMappingURL=to-response.d.ts.map
@@ -0,0 +1,24 @@
1
+ import error from "@primate/core/response/error";
2
+ import redirect from "@primate/core/response/redirect";
3
+ import view from "@primate/core/response/view";
4
+ const parse = (input) => input === null ? undefined : JSON.parse(input);
5
+ const handle_handler = (handler, response) => {
6
+ if (handler === "view") {
7
+ const { component, options, props } = response;
8
+ return view(component, parse(props), parse(options));
9
+ }
10
+ if (handler === "redirect") {
11
+ const { location, status } = response;
12
+ return redirect(location, status === null ? undefined : status);
13
+ }
14
+ const { options } = response;
15
+ return error(parse(options));
16
+ };
17
+ export default (response) => {
18
+ if (typeof response === "function") {
19
+ const { handler, ...args } = response();
20
+ return handle_handler(handler, args);
21
+ }
22
+ return JSON.parse(response);
23
+ };
24
+ //# sourceMappingURL=to-response.js.map
@@ -0,0 +1,3 @@
1
+ declare function _default(): void;
2
+ export default _default;
3
+ //# sourceMappingURL=wasm_exec.d.ts.map