@primate/go 0.3.0 → 0.4.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.
@@ -2,11 +2,9 @@ import Runtime from "#Runtime";
2
2
  import AppError from "@primate/core/AppError";
3
3
  import TAG from "@primate/core/backend/TAG";
4
4
  import fail from "@primate/core/fail";
5
- import location from "@primate/core/location";
6
5
  import log from "@primate/core/log";
7
6
  import assert from "@rcompat/assert";
8
7
  import user from "@rcompat/env/user";
9
- import runtime from "@rcompat/runtime";
10
8
  import execute from "@rcompat/stdio/execute";
11
9
  import which from "@rcompat/stdio/which";
12
10
  const COMMAND = await which("go");
@@ -19,82 +17,17 @@ const ENV = {
19
17
  const REPO = "github.com/primate-run/go";
20
18
  const [MAJOR, MINOR] = TAG.split(".").map(Number);
21
19
  const run = (wasm, go) => `${COMMAND} build -o ${wasm.name} ${go.name}`;
22
- function wrapper(path) {
23
- return `
24
- import env from "@primate/go/env";
25
- import toRequest from "@primate/go/to-request";
26
- import to_response from "@primate/go/to-response";
27
- import session from "#session";
28
- import route from "primate/route";
29
-
30
- globalThis.PRMT_SESSION = {
31
- get exists() {
32
- return session.exists;
33
- },
34
- get id() {
35
- return session.id;
36
- },
37
- get data() {
38
- return JSON.stringify(session.try());
39
- },
40
- create(data) {
41
- session.create(JSON.parse(data));
42
- },
43
- get() {
44
- return JSON.stringify(session.get());
45
- },
46
- try() {
47
- return JSON.stringify(session.try());
48
- },
49
- set(data) {
50
- session.set(JSON.parse(data));
51
- },
52
- destroy() {
53
- session.destroy();
54
- },
55
- };
56
-
57
- ${runtime === "bun"
58
- ? `import route_path from "./${path}" with { type: "file" };
59
- const binary = await Bun.file(route_path).arrayBuffer();`
60
- : `import FileRef from "primate/runtime/FileRef";
61
- const buffer = await FileRef.arrayBuffer(import.meta.url+"/../${path}");
62
- const binary = new Uint8Array(buffer);`}
63
-
64
-
65
- env();
66
-
67
- // Run Go once to register routes and get available verbs
68
- const go = new globalThis.Go();
69
- const result = await WebAssembly.instantiate(binary, go.importObject);
70
- go.run(result.instance);
71
- const verbs = globalThis.__primate_verbs || [];
72
-
73
- for (const verb of verbs) {
74
- route[verb.toLowerCase()](async request => {
75
- const requested = await toRequest(request);
76
- const _go = new globalThis.Go();
77
- return WebAssembly.instantiate(binary, {..._go.importObject}).then(_result => {
78
- _go.run(_result.instance);
79
- return to_response(globalThis.__primate_handle(verb, requested));
80
- });
81
- });
82
- }
83
- `;
84
- }
85
- ;
86
- function postlude(code) {
20
+ function postlude(code, route_id) {
87
21
  if (!code.includes(`${REPO}/route`)) {
88
22
  log.warn("Go file does not import {0} - skipping route registration", REPO);
89
23
  return code;
90
24
  }
91
25
  return `${code}
92
- func main() {
93
- route.Commit()
94
- select{}
95
- }`;
26
+ func main() {
27
+ route.Commit("${route_id}")
28
+ select{}
29
+ }`;
96
30
  }
97
- ;
98
31
  async function check_version() {
99
32
  try {
100
33
  const version = await execute(`go list -m -f '{{.Version}}' ${REPO}`);
@@ -106,11 +39,11 @@ async function check_version() {
106
39
  if (major !== MAJOR || minor !== MINOR) {
107
40
  throw fail("installed version {0} not in range {1}", trimmed, TAG);
108
41
  }
42
+ log.info("using {0} package v{1}.{2}.x", REPO, major, minor);
109
43
  }
110
44
  catch (error) {
111
45
  if (error instanceof AppError)
112
46
  throw error;
113
- console.log(error);
114
47
  throw fail("{0} dependency not found - run 'go get {0}@v{1}.0'", REPO, TAG);
115
48
  }
116
49
  }
@@ -120,12 +53,13 @@ export default class Default extends Runtime {
120
53
  app.bind(this.fileExtension, async (route, { context }) => {
121
54
  assert(context === "routes", "go: only route files are supported");
122
55
  const relative = route.debase(app.path.routes);
123
- const basename = relative.path.slice(1, -relative.extension.length);
124
- const code = postlude(await route.text());
125
- const build_go_file = app.runpath(location.routes, `${basename}.go`);
56
+ const route_id = relative.path.slice(1, -relative.extension.length);
57
+ // create a temporary .go file in the build directory for compilation
58
+ // this is necessary because `go build` requires an actual file on disk
59
+ const build_go_file = app.runpath("wasm", `${route_id}.go`);
126
60
  await build_go_file.directory.create({ recursive: true });
127
- await build_go_file.write(code);
128
- const wasm = app.runpath(location.routes, `${basename}.wasm`);
61
+ await build_go_file.write(postlude(await route.text(), route_id));
62
+ const wasm = app.runpath("wasm", `${route_id}.wasm`);
129
63
  try {
130
64
  log.info("compiling {0} to WebAssembly", route);
131
65
  await execute(run(wasm, build_go_file), {
@@ -137,7 +71,11 @@ export default class Default extends Runtime {
137
71
  throw fail("error in module {0}\n{1}", route, error);
138
72
  }
139
73
  await build_go_file.remove();
140
- return wrapper(wasm.name);
74
+ return `
75
+ import wrapper from "@primate/go/wrapper";
76
+ import bytes from "app:wasm/${route_id}.wasm" with { type: "bytes" };
77
+ await wrapper(bytes, ${JSON.stringify(route_id)});
78
+ `;
141
79
  });
142
80
  return next(app);
143
81
  }
@@ -1,9 +1,4 @@
1
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;
2
+ declare const _default: (response: any) => ResponseLike;
8
3
  export default _default;
9
4
  //# sourceMappingURL=to-response.d.ts.map
@@ -1,24 +1,32 @@
1
1
  import error from "@primate/core/response/error";
2
2
  import redirect from "@primate/core/response/redirect";
3
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
- };
4
+ const handlers = { error, redirect, view };
5
+ const is_handler = (handler) => typeof handler === "string" && Object.keys(handlers).includes(handler);
17
6
  export default (response) => {
18
- if (typeof response === "function") {
19
- const { handler, ...args } = response();
20
- return handle_handler(handler, args);
7
+ if (typeof response === "string") {
8
+ try {
9
+ response = JSON.parse(response);
10
+ }
11
+ catch {
12
+ return response;
13
+ }
14
+ }
15
+ if (response && typeof response === "object") {
16
+ const handler = response.handler;
17
+ if (is_handler(handler)) {
18
+ if (handler === "view") {
19
+ const { component, props, options } = response;
20
+ return view(component, props ? JSON.parse(props) : {}, options ? JSON.parse(options) : {});
21
+ }
22
+ if (handler === "redirect") {
23
+ const { location, status } = response;
24
+ return redirect(location, status);
25
+ }
26
+ const { options } = response;
27
+ return error(options ? JSON.parse(options) : {});
28
+ }
21
29
  }
22
- return JSON.parse(response);
30
+ return response;
23
31
  };
24
32
  //# sourceMappingURL=to-response.js.map
@@ -0,0 +1,12 @@
1
+ declare global {
2
+ var Go: {
3
+ new (): {
4
+ importObject: WebAssembly.Imports;
5
+ run(instance: WebAssembly.Instance): Promise<void>;
6
+ };
7
+ };
8
+ var PRMT_SESSION: any;
9
+ var __primate_go_initialized: Set<string> | undefined;
10
+ }
11
+ export default function wrapper(bytes: Uint8Array, routeId: string): Promise<void>;
12
+ //# sourceMappingURL=wrapper.d.ts.map
@@ -0,0 +1,52 @@
1
+ import env from "@primate/go/env";
2
+ import toRequest from "@primate/go/to-request";
3
+ import to_response from "@primate/go/to-response";
4
+ import session from "primate/config/session";
5
+ import route from "primate/route";
6
+ export default async function wrapper(bytes, routeId) {
7
+ if (!globalThis.__primate_go_initialized) {
8
+ globalThis.__primate_go_initialized = new Set();
9
+ }
10
+ globalThis.PRMT_SESSION = {
11
+ get exists() { return session().exists; },
12
+ get id() { return session().id; },
13
+ get data() { return JSON.stringify(session().try()); },
14
+ create(data) { session().create(JSON.parse(data)); },
15
+ get() { return JSON.stringify(session().get()); },
16
+ try() { return JSON.stringify(session().try()); },
17
+ set(data) { session().set(JSON.parse(data)); },
18
+ destroy() { session().destroy(); },
19
+ };
20
+ env();
21
+ const safe_route_id = routeId.replace(/\//g, "_");
22
+ const registry_name = `__primate_go_registry_${safe_route_id}`;
23
+ const call_go = `__primate_call_go_${safe_route_id}`;
24
+ delete globalThis[registry_name];
25
+ delete globalThis[call_go];
26
+ await new Promise((resolve) => {
27
+ globalThis[`__primate_go_ready_${safe_route_id}`] = () => {
28
+ resolve();
29
+ };
30
+ const go = new globalThis.Go();
31
+ WebAssembly.instantiate(bytes, go.importObject).then(result => {
32
+ go.run(result.instance).catch(err => {
33
+ console.error("Go runtime error:", routeId, err);
34
+ });
35
+ });
36
+ });
37
+ const registry_fn = globalThis[registry_name];
38
+ const registry = registry_fn();
39
+ const verbs = registry?.length
40
+ ? Array.from({ length: registry.length }, (_, i) => registry[i])
41
+ : [];
42
+ for (const verb of verbs) {
43
+ route[verb.toLowerCase()](async (request) => {
44
+ const requested = await toRequest(request);
45
+ const callFn = globalThis[call_go];
46
+ const response = callFn(verb, requested);
47
+ return to_response(response);
48
+ });
49
+ }
50
+ globalThis.__primate_go_initialized.add(routeId);
51
+ }
52
+ //# sourceMappingURL=wrapper.js.map
@@ -0,0 +1,2 @@
1
+ export { default } from "#wrapper";
2
+ //# sourceMappingURL=wrapper.d.ts.map
@@ -0,0 +1,2 @@
1
+ export { default } from "#wrapper";
2
+ //# sourceMappingURL=wrapper.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primate/go",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Primate Go backend",
5
5
  "homepage": "https://primate.run/docs/backend/go",
6
6
  "bugs": "https://github.com/primate-run/primate/issues",
@@ -18,15 +18,15 @@
18
18
  "dependencies": {
19
19
  "@rcompat/assert": "^0.3.1",
20
20
  "@rcompat/cli": "^0.11.3",
21
- "@rcompat/env": "^0.9.0",
22
- "@rcompat/fs": "^0.21.1",
21
+ "@rcompat/env": "^0.9.2",
22
+ "@rcompat/fs": "^0.22.3",
23
23
  "@rcompat/runtime": "^0.6.0",
24
24
  "@rcompat/stdio": "^0.12.2",
25
25
  "@rcompat/string": "^0.10.0",
26
- "@primate/core": "^0.3.0"
26
+ "@primate/core": "^0.4.0"
27
27
  },
28
28
  "peerDependencies": {
29
- "primate": "^0.34.0"
29
+ "primate": "^0.35.0"
30
30
  },
31
31
  "type": "module",
32
32
  "imports": {