@weborigami/origami 0.0.35

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.
Files changed (140) hide show
  1. package/LICENSE +21 -0
  2. package/ReadMe.md +3 -0
  3. package/exports/PathTransform.d.ts +5 -0
  4. package/exports/PathTransform.js +18 -0
  5. package/exports/buildExports.js +109 -0
  6. package/exports/exports.js +121 -0
  7. package/index.ts +25 -0
  8. package/package.json +40 -0
  9. package/src/builtins/!.js +21 -0
  10. package/src/builtins/@apply.js +6 -0
  11. package/src/builtins/@arrows.js +34 -0
  12. package/src/builtins/@builtins.js +18 -0
  13. package/src/builtins/@cache.js +36 -0
  14. package/src/builtins/@config.js +25 -0
  15. package/src/builtins/@copy.js +71 -0
  16. package/src/builtins/@crawl.js +507 -0
  17. package/src/builtins/@debug.js +89 -0
  18. package/src/builtins/@document.js +18 -0
  19. package/src/builtins/@equals.js +6 -0
  20. package/src/builtins/@explore.js +68 -0
  21. package/src/builtins/@false.js +1 -0
  22. package/src/builtins/@files.js +22 -0
  23. package/src/builtins/@filter.js +23 -0
  24. package/src/builtins/@globs.js +23 -0
  25. package/src/builtins/@help.js +49 -0
  26. package/src/builtins/@http.js +19 -0
  27. package/src/builtins/@https.js +19 -0
  28. package/src/builtins/@if.js +27 -0
  29. package/src/builtins/@image/format.js +5 -0
  30. package/src/builtins/@image/resize.js +5 -0
  31. package/src/builtins/@index.js +72 -0
  32. package/src/builtins/@inherited.js +17 -0
  33. package/src/builtins/@inline.js +29 -0
  34. package/src/builtins/@invoke.js +30 -0
  35. package/src/builtins/@js.js +33 -0
  36. package/src/builtins/@json.js +22 -0
  37. package/src/builtins/@loaders/css.js +4 -0
  38. package/src/builtins/@loaders/htm.js +4 -0
  39. package/src/builtins/@loaders/html.js +4 -0
  40. package/src/builtins/@loaders/js.js +14 -0
  41. package/src/builtins/@loaders/json.js +8 -0
  42. package/src/builtins/@loaders/md.js +4 -0
  43. package/src/builtins/@loaders/mjs.js +4 -0
  44. package/src/builtins/@loaders/ori.js +21 -0
  45. package/src/builtins/@loaders/orit.js +48 -0
  46. package/src/builtins/@loaders/txt.js +33 -0
  47. package/src/builtins/@loaders/xhtml.js +4 -0
  48. package/src/builtins/@loaders/yaml.js +18 -0
  49. package/src/builtins/@loaders/yml.js +4 -0
  50. package/src/builtins/@map.js +182 -0
  51. package/src/builtins/@match.js +92 -0
  52. package/src/builtins/@mdHtml.js +45 -0
  53. package/src/builtins/@new.js +6 -0
  54. package/src/builtins/@node.js +15 -0
  55. package/src/builtins/@not.js +6 -0
  56. package/src/builtins/@or.js +6 -0
  57. package/src/builtins/@ori.js +83 -0
  58. package/src/builtins/@pack.js +13 -0
  59. package/src/builtins/@parse/json.js +7 -0
  60. package/src/builtins/@parse/yaml.js +9 -0
  61. package/src/builtins/@project.js +71 -0
  62. package/src/builtins/@repeat.js +8 -0
  63. package/src/builtins/@rss.js +49 -0
  64. package/src/builtins/@scope/extend.js +22 -0
  65. package/src/builtins/@scope/get.js +25 -0
  66. package/src/builtins/@scope/invoke.js +22 -0
  67. package/src/builtins/@scope/set.js +25 -0
  68. package/src/builtins/@serve.js +74 -0
  69. package/src/builtins/@shell.js +16 -0
  70. package/src/builtins/@stdin.js +26 -0
  71. package/src/builtins/@svg.js +42 -0
  72. package/src/builtins/@tree/concat.js +21 -0
  73. package/src/builtins/@tree/count.js +24 -0
  74. package/src/builtins/@tree/defineds.js +37 -0
  75. package/src/builtins/@tree/dot.js +201 -0
  76. package/src/builtins/@tree/exceptions.js +50 -0
  77. package/src/builtins/@tree/first.js +28 -0
  78. package/src/builtins/@tree/flowSvg.js +55 -0
  79. package/src/builtins/@tree/fn.js +34 -0
  80. package/src/builtins/@tree/from.js +27 -0
  81. package/src/builtins/@tree/fromJson.js +6 -0
  82. package/src/builtins/@tree/fromYaml.js +24 -0
  83. package/src/builtins/@tree/groupBy.js +39 -0
  84. package/src/builtins/@tree/inners.js +44 -0
  85. package/src/builtins/@tree/isAsyncTree.js +17 -0
  86. package/src/builtins/@tree/keys.js +24 -0
  87. package/src/builtins/@tree/keysJson.js +44 -0
  88. package/src/builtins/@tree/map.d.ts +19 -0
  89. package/src/builtins/@tree/merge.js +47 -0
  90. package/src/builtins/@tree/mergeDeep.js +44 -0
  91. package/src/builtins/@tree/nextKey.js +29 -0
  92. package/src/builtins/@tree/parent.js +24 -0
  93. package/src/builtins/@tree/paths.js +35 -0
  94. package/src/builtins/@tree/plain.js +22 -0
  95. package/src/builtins/@tree/previousKey.js +29 -0
  96. package/src/builtins/@tree/reverse.js +51 -0
  97. package/src/builtins/@tree/setDeep.js +45 -0
  98. package/src/builtins/@tree/shuffle.js +31 -0
  99. package/src/builtins/@tree/sitemap.js +59 -0
  100. package/src/builtins/@tree/sort.js +25 -0
  101. package/src/builtins/@tree/sortBy.js +40 -0
  102. package/src/builtins/@tree/static.js +51 -0
  103. package/src/builtins/@tree/table.js +74 -0
  104. package/src/builtins/@tree/take.js +40 -0
  105. package/src/builtins/@tree/values.js +23 -0
  106. package/src/builtins/@tree/valuesDeep.js +23 -0
  107. package/src/builtins/@treeHttp.js +19 -0
  108. package/src/builtins/@treeHttps.js +19 -0
  109. package/src/builtins/@true.js +1 -0
  110. package/src/builtins/@unpack.js +13 -0
  111. package/src/builtins/@watch.js +108 -0
  112. package/src/builtins/@with.js +22 -0
  113. package/src/builtins/@yaml.js +23 -0
  114. package/src/builtins/~.js +9 -0
  115. package/src/cli/cli.js +86 -0
  116. package/src/cli/defaultModuleExport.js +16 -0
  117. package/src/cli/showUsage.js +86 -0
  118. package/src/common/CommandModulesTransform.d.ts +5 -0
  119. package/src/common/CommandModulesTransform.js +37 -0
  120. package/src/common/ConstantTree.js +17 -0
  121. package/src/common/ExplorableSiteTransform.d.ts +5 -0
  122. package/src/common/ExplorableSiteTransform.js +77 -0
  123. package/src/common/FilterTree.js +60 -0
  124. package/src/common/GlobTree.js +67 -0
  125. package/src/common/ShuffleTransform.js +29 -0
  126. package/src/common/TextDocument.js +57 -0
  127. package/src/common/addValueKeyToScope.js +30 -0
  128. package/src/common/arrowFunctionsMap.js +35 -0
  129. package/src/common/processUnpackedContent.js +39 -0
  130. package/src/common/serialize.d.ts +8 -0
  131. package/src/common/serialize.js +138 -0
  132. package/src/common/utilities.d.ts +7 -0
  133. package/src/common/utilities.js +132 -0
  134. package/src/misc/OriCommandTransform.d.ts +5 -0
  135. package/src/misc/OriCommandTransform.js +54 -0
  136. package/src/misc/assertScopeIsDefined.js +7 -0
  137. package/src/misc/explore.orit +241 -0
  138. package/src/misc/yamlOrigamiTag.js +17 -0
  139. package/src/server/mediaTypes.js +97 -0
  140. package/src/server/server.js +258 -0
@@ -0,0 +1,258 @@
1
+ import {
2
+ ObjectTree,
3
+ Tree,
4
+ isPlainObject,
5
+ isStringLike,
6
+ keysFromPath,
7
+ } from "@weborigami/async-tree";
8
+ import { Scope, extname } from "@weborigami/language";
9
+ import * as serialize from "../common/serialize.js";
10
+ import { mediaTypeForExtension, mediaTypeIsText } from "./mediaTypes.js";
11
+
12
+ const TypedArray = Object.getPrototypeOf(Uint8Array);
13
+
14
+ // Extend the tree's scope with the URL's search parameters.
15
+ function extendTreeScopeWithParams(tree, url) {
16
+ // Create a tree that includes the URL's search parameters.
17
+ const params = {};
18
+ for (const [key, value] of url.searchParams) {
19
+ params[key] = value;
20
+ }
21
+
22
+ if (Object.keys(params).length === 0) {
23
+ // No search parameters, so return the tree as is.
24
+ return tree;
25
+ }
26
+
27
+ const paramTree = new ObjectTree({
28
+ "@params": params,
29
+ });
30
+
31
+ // Create a new scope that includes search parameter tree.
32
+ const newScope = new Scope(paramTree, tree.parent);
33
+
34
+ // Create a new tree that extends the prototype chain of the supplied tree.
35
+ const extendedTree = Scope.treeWithScope(tree, newScope);
36
+
37
+ return extendedTree;
38
+ }
39
+
40
+ // Asynchronous tree router as Express middleware.
41
+ export function treeRouter(tree) {
42
+ // Return a router for the tree source.
43
+ return async function (request, response, next) {
44
+ const handled = await handleRequest(request, response, tree);
45
+ if (!handled) {
46
+ // Module not found, let next middleware function try.
47
+ next();
48
+ }
49
+ };
50
+ }
51
+
52
+ export async function handleRequest(request, response, tree) {
53
+ // For parsing purposes, we assume HTTPS -- it doesn't affect parsing.
54
+ const url = new URL(request.url, `https://${request.headers.host}`);
55
+
56
+ // We allow the use of %2F in paths as a way to insert a slash into a key, so
57
+ // we parse the path into keys first, then decode them.
58
+ const keys = keysFromPath(url.pathname).map((key) =>
59
+ typeof key === "string" ? decodeURIComponent(key) : key
60
+ );
61
+
62
+ // If the path ends with a trailing slash, the final key will be an empty
63
+ // string. Change that to "index.html".
64
+ if (keys[keys.length - 1] === "") {
65
+ keys[keys.length - 1] = "index.html";
66
+ }
67
+
68
+ const extendedTree =
69
+ url.searchParams && "parent" in tree
70
+ ? extendTreeScopeWithParams(tree, url)
71
+ : tree;
72
+
73
+ // Ask the tree for the resource with those keys.
74
+ let resource;
75
+ try {
76
+ resource = await Tree.traverse(extendedTree, ...keys);
77
+ // If resource is a function, invoke to get the object we want to return.
78
+ if (typeof resource === "function") {
79
+ resource = await resource();
80
+ }
81
+ } catch (/** @type {any} */ error) {
82
+ respondWithError(response, error);
83
+ return true;
84
+ }
85
+
86
+ let mediaType;
87
+
88
+ if (!resource) {
89
+ return false;
90
+ }
91
+
92
+ // Determine media type, what data we'll send, and encoding.
93
+ const extension = extname(url.pathname).toLowerCase();
94
+ mediaType = extension ? mediaTypeForExtension[extension] : undefined;
95
+
96
+ if (
97
+ mediaType === undefined &&
98
+ !request.url.endsWith("/") &&
99
+ (Tree.isAsyncTree(resource) ||
100
+ isPlainObject(resource) ||
101
+ resource instanceof Array)
102
+ ) {
103
+ // Redirect to an index page for the result.
104
+ // Redirect to the root of the tree.
105
+ const Location = `${request.url}/`;
106
+ response.writeHead(307, { Location });
107
+ response.end("ok");
108
+ return true;
109
+ }
110
+
111
+ // If the request is for a JSON or YAML result, and the resource we got
112
+ // isn't yet a string or Buffer, convert the resource to JSON or YAML now.
113
+ if (
114
+ (mediaType === "application/json" || mediaType === "text/yaml") &&
115
+ !isStringLike(resource)
116
+ ) {
117
+ const tree = Tree.from(resource);
118
+ resource =
119
+ mediaType === "text/yaml"
120
+ ? await serialize.toYaml(tree)
121
+ : await serialize.toJson(tree);
122
+ } else if (
123
+ mediaType === undefined &&
124
+ (isPlainObject(resource) || resource instanceof Array)
125
+ ) {
126
+ // The resource is data, try showing it as YAML.
127
+ const tree = Tree.from(resource);
128
+ resource = await serialize.toYaml(tree);
129
+ mediaType = "text/yaml";
130
+ }
131
+
132
+ let data;
133
+ if (mediaType) {
134
+ data = mediaTypeIsText[mediaType] ? String(resource) : resource;
135
+ } else {
136
+ data = textOrObject(resource);
137
+ }
138
+
139
+ if (!mediaType) {
140
+ // Can't identify media type; infer default type.
141
+ mediaType =
142
+ typeof data !== "string"
143
+ ? "application/octet-stream"
144
+ : data.trimStart().startsWith("<")
145
+ ? "text/html"
146
+ : "text/plain";
147
+ }
148
+ const encoding = mediaTypeIsText[mediaType] ? "utf-8" : undefined;
149
+
150
+ // If we didn't get back some kind of data that response.write() accepts,
151
+ // assume it was an error.
152
+ const validResponse = typeof data === "string" || data instanceof TypedArray;
153
+
154
+ if (!validResponse) {
155
+ const typeName = data.constructor?.name ?? typeof data;
156
+ console.error(
157
+ `A served tree must return a string or a TypedArray (such as a Buffer) but returned an instance of ${typeName}.`
158
+ );
159
+ return false;
160
+ }
161
+
162
+ response.writeHead(200, {
163
+ "Content-Type": mediaType,
164
+ });
165
+ try {
166
+ response.end(data, encoding);
167
+ } catch (/** @type {any} */ error) {
168
+ console.error(error.message);
169
+ return false;
170
+ }
171
+
172
+ return true;
173
+ }
174
+
175
+ /**
176
+ * A request listener for use with the node http.createServer and
177
+ * https.createServer calls, letting you serve an async tree as a set of pages.
178
+ *
179
+ * @typedef {import("@weborigami/async-tree").Treelike} Treelike
180
+ * @param {Treelike} treelike
181
+ */
182
+ export function requestListener(treelike) {
183
+ const tree = Tree.from(treelike);
184
+ return async function (request, response) {
185
+ console.log(decodeURI(request.url));
186
+ const handled = await handleRequest(request, response, tree);
187
+ if (!handled) {
188
+ // Ignore exceptions that come up with sending a Not Found response.
189
+ try {
190
+ response.writeHead(404, { "Content-Type": "text/html" });
191
+ response.end(`Not found`, "utf-8");
192
+ } catch (error) {}
193
+ }
194
+ };
195
+ }
196
+
197
+ /**
198
+ * Construct a page in response in the given error, and also show the error in
199
+ * the console.
200
+ */
201
+ function respondWithError(response, error) {
202
+ let message = "";
203
+ // Work up to the root cause, displaying intermediate messages as we go up.
204
+ while (error.cause) {
205
+ message += error.message + `\n`;
206
+ error = error.cause;
207
+ }
208
+ if (error.name) {
209
+ message += `${error.name}: `;
210
+ }
211
+ message += error.message;
212
+ // Prevent HTML in the error message from being interpreted as HTML.
213
+ message = message.replace(/</g, "&lt;").replace(/>/g, "&gt;");
214
+ const html = `<!DOCTYPE html>
215
+ <html>
216
+ <head>
217
+ <title>Error: ${error.message}</title>
218
+ </head>
219
+ <body>
220
+ <h1>Error</h1>
221
+ <pre><code>
222
+ ${message}
223
+ </code></pre>
224
+ </body>
225
+ </html>
226
+ `;
227
+ response.writeHead(404, { "Content-Type": "text/html" });
228
+ response.end(html, "utf-8");
229
+ console.error(message);
230
+ }
231
+
232
+ /**
233
+ * Convert to a string if we can, but leave objects that convert to something
234
+ * like "[object Object]" alone.
235
+ *
236
+ * @param {any} obj
237
+ */
238
+ function textOrObject(obj) {
239
+ if (typeof obj === "string") {
240
+ // Return string as is.
241
+ return obj;
242
+ }
243
+
244
+ // See if we can convert the object to a string.
245
+ const text = String(obj);
246
+
247
+ // See if we ended up with a default string.
248
+ const constructor = obj.constructor;
249
+ const name = constructor.name || "Object";
250
+ if (text === `[object Object]` || text === `[object ${name}]`) {
251
+ // Got a default string, so probably not what we wanted.
252
+ // Return original object.
253
+ return obj;
254
+ } else {
255
+ // We appear to have cast the object to a string; return that.
256
+ return text;
257
+ }
258
+ }