@primate/python 0.1.4 → 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.
- package/lib/private/Default.d.ts +7 -0
- package/lib/private/Default.js +100 -0
- package/lib/private/Runtime.d.ts +6 -0
- package/lib/private/Runtime.js +6 -0
- package/lib/private/borrow.d.ts +4 -0
- package/lib/private/borrow.js +8 -0
- package/lib/private/handler-property.d.ts +3 -0
- package/lib/private/handler-property.js +2 -0
- package/lib/private/helpers.d.ts +5 -0
- package/lib/private/helpers.js +22 -0
- package/lib/private/pyodide.d.ts +2 -0
- package/lib/private/pyodide.js +5 -0
- package/lib/private/to-request.d.ts +41 -0
- package/lib/private/to-request.js +102 -0
- package/lib/private/to-response.d.ts +6 -0
- package/lib/private/to-response.js +27 -0
- package/lib/private/unwrap.d.ts +4 -0
- package/lib/private/unwrap.js +28 -0
- package/lib/public/borrow.d.ts +2 -0
- package/lib/public/borrow.js +2 -0
- package/lib/public/default.d.ts +4 -0
- package/lib/public/default.js +3 -0
- package/lib/public/helpers.d.ts +2 -0
- package/lib/public/helpers.js +2 -0
- package/lib/public/pyodide.d.ts +2 -0
- package/lib/public/pyodide.js +2 -0
- package/lib/public/runtime.d.ts +4 -0
- package/lib/public/runtime.js +3 -0
- package/lib/public/to-request.d.ts +2 -0
- package/lib/public/to-request.js +2 -0
- package/lib/public/to-response.d.ts +2 -0
- package/lib/public/to-response.js +2 -0
- package/package.json +26 -15
- package/src/default.js +0 -8
- package/src/load.js +0 -1
- package/src/private/build.js +0 -42
- package/src/private/extension.js +0 -1
- package/src/private/pkgname.js +0 -1
- package/src/runtime.js +0 -3
- package/src/to-request.js +0 -78
- package/src/to-response.js +0 -31
- package/src/unwrap.js +0 -17
- package/src/wrap.js +0 -24
|
@@ -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): Promise<BuildApp>;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=Default.d.ts.map
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import Runtime from "#Runtime";
|
|
2
|
+
import wrap from "@primate/core/route/wrap";
|
|
3
|
+
import assert from "@rcompat/assert";
|
|
4
|
+
import FileRef from "@rcompat/fs/FileRef";
|
|
5
|
+
const js_wrapper = async (fileRef, packages) => {
|
|
6
|
+
const userPythonRaw = await fileRef.text();
|
|
7
|
+
const user_code = userPythonRaw.replace(/`/g, "\\`").replace(/\\/g, "\\\\");
|
|
8
|
+
return `
|
|
9
|
+
import route from "primate/route";
|
|
10
|
+
import to_request from "@primate/python/to-request";
|
|
11
|
+
import to_response from "@primate/python/to-response";
|
|
12
|
+
import session from "primate/config/session";
|
|
13
|
+
import helpers from "@primate/python/helpers";
|
|
14
|
+
import pyodide from "@primate/python/pyodide";
|
|
15
|
+
import borrow from "@primate/python/borrow";
|
|
16
|
+
|
|
17
|
+
const wrapped_session = {
|
|
18
|
+
get id() {
|
|
19
|
+
return session().id;
|
|
20
|
+
},
|
|
21
|
+
get exists() {
|
|
22
|
+
return session().exists;
|
|
23
|
+
},
|
|
24
|
+
create(initial) {
|
|
25
|
+
session().create(borrow(initial));
|
|
26
|
+
},
|
|
27
|
+
get() {
|
|
28
|
+
return session().get();
|
|
29
|
+
},
|
|
30
|
+
try() {
|
|
31
|
+
return session().get();
|
|
32
|
+
},
|
|
33
|
+
set(data) {
|
|
34
|
+
session().set(borrow(data));
|
|
35
|
+
},
|
|
36
|
+
destroy() {
|
|
37
|
+
session().destroy();
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const python = await pyodide();
|
|
42
|
+
const messageCallback = () => {};
|
|
43
|
+
|
|
44
|
+
await python.loadPackage("micropip", { messageCallback });
|
|
45
|
+
const micropip = python.pyimport("micropip");
|
|
46
|
+
await micropip.install("primate-run", { messageCallback });
|
|
47
|
+
${packages.map(p => `await micropip.install("${p}", { messageCallback });`).join("\n")}
|
|
48
|
+
|
|
49
|
+
await python.runPython(\`${user_code}\`);
|
|
50
|
+
|
|
51
|
+
// Get the registry of registered route function names
|
|
52
|
+
const registry = python.runPython("Route.registry()").toJs();
|
|
53
|
+
|
|
54
|
+
// Create route handler functions
|
|
55
|
+
await python.runPython(\`
|
|
56
|
+
\${Object.keys(registry).map(route => \`
|
|
57
|
+
def run_\${route.toUpperCase()}(js_request, helpers_obj, session_obj):
|
|
58
|
+
Route.set_session(session_obj, helpers_obj)
|
|
59
|
+
request = Route.Request(js_request, helpers_obj)
|
|
60
|
+
return Route.call_route("\${route.toUpperCase()}", request)
|
|
61
|
+
\`).join("\\n")}
|
|
62
|
+
\`);
|
|
63
|
+
|
|
64
|
+
// Create route handlers for each registered route
|
|
65
|
+
for (const [verb, func_name] of Object.entries(registry)) {
|
|
66
|
+
const route_fn = python.globals.get(\`run_\${verb.toUpperCase()}\`);
|
|
67
|
+
|
|
68
|
+
route[verb.toLowerCase()](async request => {
|
|
69
|
+
try {
|
|
70
|
+
const converted_request = await to_request(request);
|
|
71
|
+
const result = await route_fn(converted_request, helpers, wrapped_session);
|
|
72
|
+
return to_response(result);
|
|
73
|
+
} catch (e) {
|
|
74
|
+
console.error(\`python error (\${verb.toLowerCase()})\`, e);
|
|
75
|
+
return { status: 500, body: "Python execution error: " + e.message };
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
`;
|
|
80
|
+
};
|
|
81
|
+
export default class Default extends Runtime {
|
|
82
|
+
async build(app, next) {
|
|
83
|
+
const requirements_txt = app.root.join("requirements.txt");
|
|
84
|
+
let packages = [];
|
|
85
|
+
if (await requirements_txt.exists()) {
|
|
86
|
+
const requirements = await FileRef.text(requirements_txt);
|
|
87
|
+
packages = requirements
|
|
88
|
+
.split("\n")
|
|
89
|
+
.filter(line => line.trim() && !line.startsWith("#"))
|
|
90
|
+
.map(p => p.trim());
|
|
91
|
+
}
|
|
92
|
+
app.bind(this.fileExtension, async (route, { build, context }) => {
|
|
93
|
+
assert(context === "routes", "python: only route files are supported");
|
|
94
|
+
const code = wrap(await js_wrapper(route, packages), route, build);
|
|
95
|
+
await route.append(".js").write(code);
|
|
96
|
+
});
|
|
97
|
+
return next(app);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=Default.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
function integer(n) {
|
|
2
|
+
return Number.isInteger(n);
|
|
3
|
+
}
|
|
4
|
+
export default {
|
|
5
|
+
type(value) {
|
|
6
|
+
if (typeof value === "number")
|
|
7
|
+
return integer(value) ? "integer" : "float";
|
|
8
|
+
if (typeof value === "boolean")
|
|
9
|
+
return "boolean";
|
|
10
|
+
if (typeof value === "string")
|
|
11
|
+
return "string";
|
|
12
|
+
if (typeof value === "object") {
|
|
13
|
+
if (Array.isArray(value))
|
|
14
|
+
return "array";
|
|
15
|
+
if (value === null)
|
|
16
|
+
return "nil";
|
|
17
|
+
return "object";
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type RequestFacade from "@primate/core/request/RequestFacade";
|
|
2
|
+
import type Dict from "@rcompat/type/Dict";
|
|
3
|
+
type FileEntry = {
|
|
4
|
+
bytes: Uint8Array;
|
|
5
|
+
field: string;
|
|
6
|
+
name: string;
|
|
7
|
+
size: number;
|
|
8
|
+
type: string;
|
|
9
|
+
};
|
|
10
|
+
export default function to_request(request: RequestFacade): Promise<{
|
|
11
|
+
url: {
|
|
12
|
+
href: string;
|
|
13
|
+
origin: string;
|
|
14
|
+
protocol: string;
|
|
15
|
+
username: string;
|
|
16
|
+
password: string;
|
|
17
|
+
host: string;
|
|
18
|
+
hostname: string;
|
|
19
|
+
port: string;
|
|
20
|
+
pathname: string;
|
|
21
|
+
search: string;
|
|
22
|
+
hash: string;
|
|
23
|
+
};
|
|
24
|
+
body: {
|
|
25
|
+
text: () => string;
|
|
26
|
+
json: () => string;
|
|
27
|
+
fields: () => Record<string, string>;
|
|
28
|
+
files: () => FileEntry[];
|
|
29
|
+
binary: () => {
|
|
30
|
+
buffer: Uint8Array<ArrayBuffer>;
|
|
31
|
+
mime: string;
|
|
32
|
+
};
|
|
33
|
+
none: () => null;
|
|
34
|
+
};
|
|
35
|
+
path: Dict;
|
|
36
|
+
query: Dict;
|
|
37
|
+
headers: Dict;
|
|
38
|
+
cookies: Dict;
|
|
39
|
+
}>;
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=to-request.d.ts.map
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
function plainUrl(u) {
|
|
2
|
+
return {
|
|
3
|
+
href: u.href,
|
|
4
|
+
origin: u.origin,
|
|
5
|
+
protocol: u.protocol,
|
|
6
|
+
username: u.username,
|
|
7
|
+
password: u.password,
|
|
8
|
+
host: u.host,
|
|
9
|
+
hostname: u.hostname,
|
|
10
|
+
port: u.port,
|
|
11
|
+
pathname: u.pathname,
|
|
12
|
+
search: u.search,
|
|
13
|
+
hash: u.hash,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function toDict(obj) {
|
|
17
|
+
const out = Object.create(null);
|
|
18
|
+
if (obj instanceof URLSearchParams) {
|
|
19
|
+
obj.forEach((v, k) => {
|
|
20
|
+
if (!(k in out))
|
|
21
|
+
out[k] = v ?? "";
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
26
|
+
out[k] = v == null ? "" : String(v);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
async function bridgeFields(body) {
|
|
32
|
+
const fields = body.fields(); // can be strings or File
|
|
33
|
+
const plain = Object.create(null);
|
|
34
|
+
const files = [];
|
|
35
|
+
const pending = [];
|
|
36
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
37
|
+
if (typeof v === "string") {
|
|
38
|
+
plain[k] = v;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
// v is File
|
|
42
|
+
const name = v.name;
|
|
43
|
+
const type = v.type;
|
|
44
|
+
const size = v.size;
|
|
45
|
+
pending.push(v.arrayBuffer().then(buffer => {
|
|
46
|
+
files.push({
|
|
47
|
+
bytes: new Uint8Array(buffer),
|
|
48
|
+
field: k,
|
|
49
|
+
name,
|
|
50
|
+
size,
|
|
51
|
+
type,
|
|
52
|
+
});
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
await Promise.all(pending);
|
|
57
|
+
return {
|
|
58
|
+
fields: () => plain,
|
|
59
|
+
files: () => files,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
async function bridgeBody(body) {
|
|
63
|
+
let fields, buffer, blob;
|
|
64
|
+
if (body.type === "fields") {
|
|
65
|
+
fields = await bridgeFields(body);
|
|
66
|
+
}
|
|
67
|
+
if (body.type === "binary") {
|
|
68
|
+
blob = body.binary();
|
|
69
|
+
buffer = new Uint8Array(await blob.arrayBuffer());
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
text: () => body.text(),
|
|
73
|
+
json: () => {
|
|
74
|
+
return JSON.stringify(body.json());
|
|
75
|
+
},
|
|
76
|
+
fields: () => fields.fields(),
|
|
77
|
+
files: () => fields.files(),
|
|
78
|
+
binary: () => {
|
|
79
|
+
const mime = blob.type || "application/octet-stream";
|
|
80
|
+
return {
|
|
81
|
+
buffer,
|
|
82
|
+
mime,
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
none: () => null,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
export default async function to_request(request) {
|
|
89
|
+
const body = await bridgeBody(request.body);
|
|
90
|
+
const cookies = toDict(request.cookies.toJSON());
|
|
91
|
+
const headers = toDict(request.headers.toJSON());
|
|
92
|
+
const path = toDict(request.path.toJSON());
|
|
93
|
+
const query = toDict(request.query.toJSON());
|
|
94
|
+
const u = request.url;
|
|
95
|
+
const url = plainUrl(new URL(String(u)));
|
|
96
|
+
return {
|
|
97
|
+
url,
|
|
98
|
+
body,
|
|
99
|
+
path, query, headers, cookies,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=to-request.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type ResponseLike from "@primate/core/response/ResponseLike";
|
|
2
|
+
import type Dict from "@rcompat/type/Dict";
|
|
3
|
+
import type { PyProxy } from "pyodide/ffi";
|
|
4
|
+
declare const _default: (response: Dict | PyProxy) => ResponseLike;
|
|
5
|
+
export default _default;
|
|
6
|
+
//# sourceMappingURL=to-response.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import HANDLER_PROPERTY from "#handler-property";
|
|
2
|
+
import unwrap from "#unwrap";
|
|
3
|
+
import error from "@primate/core/response/error";
|
|
4
|
+
import redirect from "@primate/core/response/redirect";
|
|
5
|
+
import view from "@primate/core/response/view";
|
|
6
|
+
const handlers = { error, redirect, view };
|
|
7
|
+
const handle_handler = (handler, response) => {
|
|
8
|
+
if (handler === "view") {
|
|
9
|
+
const { name, options, props } = response;
|
|
10
|
+
return view(name, props, options);
|
|
11
|
+
}
|
|
12
|
+
if (handler === "redirect") {
|
|
13
|
+
const { location, status } = response;
|
|
14
|
+
return redirect(location, status);
|
|
15
|
+
}
|
|
16
|
+
const { options } = response;
|
|
17
|
+
return error(options);
|
|
18
|
+
};
|
|
19
|
+
const is_handler = (handler) => typeof handler === "string" && Object.keys(handlers).includes(handler);
|
|
20
|
+
export default (response) => {
|
|
21
|
+
const unwrapped = unwrap(response);
|
|
22
|
+
const handler = unwrapped[HANDLER_PROPERTY];
|
|
23
|
+
return is_handler(handler)
|
|
24
|
+
? handle_handler(handler, unwrapped)
|
|
25
|
+
: unwrapped;
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=to-response.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const dict_converter = (value) => Object.fromEntries(value);
|
|
2
|
+
const normalize = (response) => response instanceof Map ? Object.fromEntries(response.entries()) : response;
|
|
3
|
+
const recursively_convert = (input) => {
|
|
4
|
+
return Object.fromEntries(Object.entries(input).map(([k, o]) => {
|
|
5
|
+
if (o.toJs !== undefined) {
|
|
6
|
+
return [k, o.toJs({ dict_converter })];
|
|
7
|
+
}
|
|
8
|
+
return [k, o];
|
|
9
|
+
}));
|
|
10
|
+
};
|
|
11
|
+
const qualify = (response, destroy = true) => {
|
|
12
|
+
if (response.toJs !== undefined) {
|
|
13
|
+
const py_proxy = response;
|
|
14
|
+
const converted = py_proxy.toJs({ dict_converter });
|
|
15
|
+
destroy && py_proxy.destroy();
|
|
16
|
+
return converted;
|
|
17
|
+
}
|
|
18
|
+
if (typeof response === "object") {
|
|
19
|
+
return recursively_convert(response);
|
|
20
|
+
}
|
|
21
|
+
return response;
|
|
22
|
+
};
|
|
23
|
+
export default function unwrap(response) {
|
|
24
|
+
return normalize(qualify(response));
|
|
25
|
+
}
|
|
26
|
+
/*export const unwrap_async = (response: PyProxy) =>
|
|
27
|
+
normalize(qualify(response, false));*/
|
|
28
|
+
//# sourceMappingURL=unwrap.js.map
|
package/package.json
CHANGED
|
@@ -1,39 +1,50 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primate/python",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Primate Python backend",
|
|
5
|
-
"homepage": "https://
|
|
6
|
-
"bugs": "https://github.com/
|
|
5
|
+
"homepage": "https://primate.run/docs/backend/python",
|
|
6
|
+
"bugs": "https://github.com/primate-run/primate/issues",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"files": [
|
|
9
|
-
"
|
|
10
|
-
"
|
|
9
|
+
"/lib/**/*.js",
|
|
10
|
+
"/lib/**/*.d.ts",
|
|
11
|
+
"!/**/*.spec.*"
|
|
11
12
|
],
|
|
12
13
|
"repository": {
|
|
13
14
|
"type": "git",
|
|
14
|
-
"url": "https://github.com/
|
|
15
|
+
"url": "https://github.com/primate-run/primate",
|
|
15
16
|
"directory": "packages/python"
|
|
16
17
|
},
|
|
17
18
|
"dependencies": {
|
|
18
|
-
"@rcompat/
|
|
19
|
-
"
|
|
20
|
-
"
|
|
19
|
+
"@rcompat/assert": "^0.3.1",
|
|
20
|
+
"@rcompat/fs": "^0.21.1",
|
|
21
|
+
"pyodide": "^0.28.3",
|
|
22
|
+
"@primate/core": "^0.2.0",
|
|
23
|
+
"pema": "^0.2.0"
|
|
21
24
|
},
|
|
22
25
|
"peerDependencies": {
|
|
23
|
-
"primate": "^0.
|
|
26
|
+
"primate": "^0.33.0"
|
|
24
27
|
},
|
|
25
28
|
"type": "module",
|
|
26
29
|
"imports": {
|
|
27
30
|
"#*": {
|
|
28
|
-
"
|
|
29
|
-
"default": "./
|
|
31
|
+
"apekit": "./src/private/*.ts",
|
|
32
|
+
"default": "./lib/private/*.js"
|
|
30
33
|
}
|
|
31
34
|
},
|
|
32
35
|
"exports": {
|
|
33
36
|
".": {
|
|
34
|
-
"runtime": "./
|
|
35
|
-
"default": "./
|
|
37
|
+
"runtime": "./lib/public/runtime.js",
|
|
38
|
+
"default": "./lib/public/default.js"
|
|
36
39
|
},
|
|
37
|
-
"./*":
|
|
40
|
+
"./*": {
|
|
41
|
+
"apekit": "./src/public/*.ts",
|
|
42
|
+
"default": "./lib/public/*.js"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "npm run clean && tsc",
|
|
47
|
+
"clean": "rm -rf ./lib",
|
|
48
|
+
"lint": "eslint ."
|
|
38
49
|
}
|
|
39
50
|
}
|
package/src/default.js
DELETED
package/src/load.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { loadPyodide as default } from "pyodide";
|
package/src/private/build.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
const routes_re = /def (?<route>get|post|put|delete)/gu;
|
|
2
|
-
const get_routes = code => [...code.matchAll(routes_re)]
|
|
3
|
-
.map(({ groups: { route } }) => route);
|
|
4
|
-
|
|
5
|
-
const make_route = route => `async ${route.toLowerCase()}(request) {
|
|
6
|
-
const ${route}_fn = pyodide.globals.get("${route}");
|
|
7
|
-
return to_response(await ${route}_fn(to_request(pyodide.toPy, request)));
|
|
8
|
-
}`;
|
|
9
|
-
|
|
10
|
-
const make_package = pkg => `await pyodide.loadPackage("${pkg}", {
|
|
11
|
-
messageCallback: _ => _,
|
|
12
|
-
});\n`;
|
|
13
|
-
|
|
14
|
-
const js_wrapper = async (path, routes, packages) => `
|
|
15
|
-
import file from "primate/runtime/file";
|
|
16
|
-
import to_request from "@primate/python/to-request";
|
|
17
|
-
import to_response from "@primate/python/to-response";
|
|
18
|
-
import wrap from "@primate/python/wrap";
|
|
19
|
-
import load from "@primate/python/load";
|
|
20
|
-
|
|
21
|
-
const pyodide = await load({ indexURL: "./node_modules/pyodide" });
|
|
22
|
-
const python_route = await file(${JSON.stringify(path)}).text();
|
|
23
|
-
${packages.map(make_package)}
|
|
24
|
-
pyodide.runPython(wrap(python_route));
|
|
25
|
-
export default {
|
|
26
|
-
${routes.map(route => make_route(route)).join(",\n")}
|
|
27
|
-
};
|
|
28
|
-
`;
|
|
29
|
-
|
|
30
|
-
export default ({ extension, packages }) => async (app, next) => {
|
|
31
|
-
app.bind(extension, async (directory, file) => {
|
|
32
|
-
const path = directory.join(file);
|
|
33
|
-
const base = path.directory;
|
|
34
|
-
const js = path.base.concat(".js");
|
|
35
|
-
const code = await path.text();
|
|
36
|
-
const routes = get_routes(code);
|
|
37
|
-
// write .js wrapper
|
|
38
|
-
await base.join(js).write(await js_wrapper(`${path}`, routes, packages));
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
return next(app);
|
|
42
|
-
};
|
package/src/private/extension.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default ".py";
|
package/src/private/pkgname.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default "@primate/python";
|
package/src/runtime.js
DELETED
package/src/to-request.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { unwrap, unwrap_async } from "./unwrap.js";
|
|
2
|
-
|
|
3
|
-
const dispatchers = ["path", "query", "headers", "cookies"];
|
|
4
|
-
|
|
5
|
-
const wrap_dispatchers = (toPy, request) =>
|
|
6
|
-
Object.fromEntries(dispatchers.map(dispatcher =>
|
|
7
|
-
[dispatcher, {
|
|
8
|
-
...request[dispatcher],
|
|
9
|
-
json() {
|
|
10
|
-
return toPy(request[dispatcher].json());
|
|
11
|
-
},
|
|
12
|
-
}]));
|
|
13
|
-
|
|
14
|
-
const wrap_store = (toPy, store) => {
|
|
15
|
-
return {
|
|
16
|
-
...store,
|
|
17
|
-
async validate(input) {
|
|
18
|
-
return toPy(await store.validate(unwrap_async(input)));
|
|
19
|
-
},
|
|
20
|
-
async get(value) {
|
|
21
|
-
return toPy(await store.get(unwrap_async(value)));
|
|
22
|
-
},
|
|
23
|
-
async count(criteria) {
|
|
24
|
-
return store.count(unwrap_async(criteria));
|
|
25
|
-
},
|
|
26
|
-
async find(criteria) {
|
|
27
|
-
return toPy(await store.find(unwrap_async(criteria)));
|
|
28
|
-
},
|
|
29
|
-
async exists(criteria) {
|
|
30
|
-
return store.exists(unwrap_async(criteria));
|
|
31
|
-
},
|
|
32
|
-
async insert(document) {
|
|
33
|
-
return toPy(await store.insert(unwrap_async(document)));
|
|
34
|
-
},
|
|
35
|
-
async update(criteria, document) {
|
|
36
|
-
const ua = unwrap_async;
|
|
37
|
-
return toPy(await store.update(ua(criteria), ua(document)));
|
|
38
|
-
},
|
|
39
|
-
async save(document) {
|
|
40
|
-
return toPy(await store.save(unwrap_async(document)));
|
|
41
|
-
},
|
|
42
|
-
async delete(criteria) {
|
|
43
|
-
return store.delete(unwrap_async(criteria));
|
|
44
|
-
},
|
|
45
|
-
schema: {
|
|
46
|
-
async create(description) {
|
|
47
|
-
return store.schema.create(unwrap_async(description));
|
|
48
|
-
},
|
|
49
|
-
async delete() {
|
|
50
|
-
return store.schema.delete();
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const is_store = value => value.connection !== undefined;
|
|
57
|
-
const wrap_stores = (toPy, object) => Object.entries(object)
|
|
58
|
-
.reduce((reduced, [key, value]) => ({
|
|
59
|
-
...reduced,
|
|
60
|
-
[key]: is_store(value) ? wrap_store(toPy, value) : wrap_stores(toPy, value),
|
|
61
|
-
}), {});
|
|
62
|
-
|
|
63
|
-
const wrap_session = (toPy, { session }) => ({
|
|
64
|
-
...session,
|
|
65
|
-
create(data) {
|
|
66
|
-
session.create(unwrap(data));
|
|
67
|
-
},
|
|
68
|
-
json() {
|
|
69
|
-
return toPy(session.json());
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
export default (toPy, request) => ({
|
|
74
|
-
...request,
|
|
75
|
-
...wrap_dispatchers(toPy, request),
|
|
76
|
-
session: wrap_session(toPy, request),
|
|
77
|
-
store: wrap_stores(toPy, request.store),
|
|
78
|
-
});
|
package/src/to-response.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import error from "@primate/core/handler/error";
|
|
2
|
-
import redirect from "@primate/core/handler/redirect";
|
|
3
|
-
import view from "@primate/core/handler/view";
|
|
4
|
-
|
|
5
|
-
import { unwrap } from "./unwrap.js";
|
|
6
|
-
|
|
7
|
-
const handlers = {
|
|
8
|
-
view({ name, props = {}, options = {} }) {
|
|
9
|
-
return view(name, props, options);
|
|
10
|
-
},
|
|
11
|
-
redirect({ location, options }) {
|
|
12
|
-
return redirect(location, options);
|
|
13
|
-
},
|
|
14
|
-
error({ body, options }) {
|
|
15
|
-
return error(body, options);
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const handle_handler = response => {
|
|
20
|
-
const { __handler__ : handler, ...args } = response;
|
|
21
|
-
return handlers[handler]?.(args) ?? error();
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const handle_other = response => response;
|
|
25
|
-
|
|
26
|
-
export default raw_response => {
|
|
27
|
-
const response = unwrap(raw_response);
|
|
28
|
-
return response.__handler__ === undefined
|
|
29
|
-
? handle_other(response)
|
|
30
|
-
: handle_handler(response);
|
|
31
|
-
};
|
package/src/unwrap.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
const dict_converter = value => Object.fromEntries(value);
|
|
2
|
-
|
|
3
|
-
const normalize = response =>
|
|
4
|
-
response instanceof Map ? Object.fromEntries(response.entries()) : response;
|
|
5
|
-
const qualify = (response, destroy = true) => {
|
|
6
|
-
if (response?.toJs !== undefined) {
|
|
7
|
-
const py_proxy = response;
|
|
8
|
-
const converted = py_proxy.toJs({ dict_converter });
|
|
9
|
-
destroy && py_proxy.destroy();
|
|
10
|
-
return converted;
|
|
11
|
-
}
|
|
12
|
-
return response;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export const unwrap = response => normalize(qualify(response));
|
|
16
|
-
|
|
17
|
-
export const unwrap_async = response => normalize(qualify(response, false));
|
package/src/wrap.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export default code => `
|
|
2
|
-
class Primate():
|
|
3
|
-
@staticmethod
|
|
4
|
-
def view(name, props = None, options = None):
|
|
5
|
-
return {
|
|
6
|
-
"__handler__": "view",
|
|
7
|
-
"name": name,
|
|
8
|
-
"props": props,
|
|
9
|
-
"options": options,
|
|
10
|
-
}
|
|
11
|
-
@staticmethod
|
|
12
|
-
def redirect(location, options = None):
|
|
13
|
-
return {
|
|
14
|
-
"__handler__": "redirect",
|
|
15
|
-
"location": location,
|
|
16
|
-
"options": options,
|
|
17
|
-
}
|
|
18
|
-
@staticmethod
|
|
19
|
-
def error(body, options = None):
|
|
20
|
-
return {
|
|
21
|
-
"__handler__": "error",
|
|
22
|
-
"body": body,
|
|
23
|
-
"options": options,
|
|
24
|
-
}\n${code}`;
|