@spirobel/mininext 0.2.0 → 0.2.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.
@@ -0,0 +1,125 @@
1
+ import { url, Mini, type HtmlHandler } from "./url";
2
+ import { html, isError, HtmlString, head } from "./html";
3
+ import { watch } from "fs/promises";
4
+ import * as path from "path";
5
+ const PROJECT_ROOT = import.meta.dir + "/../";
6
+
7
+ async function build(backendPath: string = "backend/backend.ts") {
8
+ await buildBackend(backendPath);
9
+ if (Bun.argv[2] === "dev") {
10
+ await devServer();
11
+ }
12
+ }
13
+ import type { BunPlugin } from "bun";
14
+
15
+ const myPlugin: BunPlugin = {
16
+ name: "node buffer in the frontend",
17
+ setup(build) {
18
+ build.onResolve({ filter: /^buffer$/ }, (args) => {
19
+ const path_to_buffer_lib = path.resolve(
20
+ PROJECT_ROOT,
21
+ "node_modules/buffer/index.js"
22
+ );
23
+ if (path_to_buffer_lib)
24
+ return {
25
+ path: path_to_buffer_lib,
26
+ };
27
+ });
28
+ },
29
+ };
30
+ async function buildBackend(backendPath: string = "backend/backend.ts") {
31
+ global.FrontendScriptUrls = [];
32
+ global.FrontendScripts = [];
33
+ global.bundledSVGs = {};
34
+ const i = await import(path.resolve(PROJECT_ROOT, backendPath));
35
+
36
+ for (const frontend of url.getFrontends()) {
37
+ const f = await buildFrontend(frontend);
38
+ FrontendScriptUrls.push("/" + f.url);
39
+ FrontendScripts.push(f.script);
40
+ }
41
+ for (const svgPath of url.getSvgPaths()) {
42
+ const parsedSvgPath = path.parse(svgPath);
43
+ const svgContent = Bun.file(path.join(PROJECT_ROOT + "/backend/", svgPath));
44
+ const svgHash = Bun.hash(await svgContent.arrayBuffer());
45
+ const svgUrl = `/${parsedSvgPath.name}-${svgHash}.svg`;
46
+ bundledSVGs[svgUrl] = {
47
+ svgContent: await svgContent.text(),
48
+ svgPath,
49
+ };
50
+ }
51
+ const res = await Bun.build({
52
+ entrypoints: [path.resolve(PROJECT_ROOT, backendPath)],
53
+ outdir: path.resolve(PROJECT_ROOT, "dist"),
54
+ naming: "backend.js",
55
+ minify: Bun.argv[2] === "dev" ? false : true, //production
56
+ target: "node",
57
+ define: {
58
+ FrontendScripts: JSON.stringify(FrontendScripts),
59
+ FrontendScriptUrls: JSON.stringify(FrontendScriptUrls),
60
+ bundledSVGs: JSON.stringify(bundledSVGs),
61
+ },
62
+ });
63
+ }
64
+
65
+ async function buildFrontend(file: string) {
66
+ const result = await Bun.build({
67
+ entrypoints: [path.resolve(PROJECT_ROOT, `frontend/${file}`)],
68
+ outdir: path.resolve(PROJECT_ROOT, "dist"),
69
+ naming: "[name]-[hash].[ext]",
70
+ minify: Bun.argv[2] === "dev" ? false : true, //production
71
+ target: "browser",
72
+ plugins: [myPlugin],
73
+ });
74
+ if (!result?.outputs[0]?.path) console.log(result);
75
+ const url = path.basename(result.outputs[0].path);
76
+ //results.push({ file, p });
77
+ return { url, script: await result.outputs[0].text() };
78
+ }
79
+
80
+ async function devServer() {
81
+ //start the reloader and tell browser to refresh once
82
+ await buildBackend();
83
+ let refreshed_once = false;
84
+ const server = Bun.serve({
85
+ port: 3001,
86
+ fetch(request) {
87
+ const success: Boolean = server.upgrade(request);
88
+ return success
89
+ ? new Response("Reloader works!")
90
+ : new Response("Reloader WebSocket upgrade error", { status: 400 });
91
+ },
92
+ websocket: {
93
+ open(ws) {
94
+ ws.subscribe("reloader");
95
+ if (!refreshed_once) {
96
+ ws.send("Reload!");
97
+ refreshed_once = true;
98
+ }
99
+ },
100
+ message(ws, message) {}, // a message is received
101
+ },
102
+ });
103
+ async function watchAndBuild(dir: string) {
104
+ try {
105
+ //start the file watcher that will rebuild frontend on save
106
+ const watcher = watch(path.resolve(PROJECT_ROOT, dir), {
107
+ recursive: true,
108
+ });
109
+ for await (const event of watcher) {
110
+ buildBackend().then(() => {
111
+ // tell browser to refresh again because we saw a change
112
+ server.publish("reloader", "Reload!");
113
+ });
114
+ }
115
+ } catch (e) {
116
+ console.log(
117
+ `mini-next dev server has trouble watching "./${dir}", does the directory exist?`
118
+ );
119
+ }
120
+ }
121
+ watchAndBuild("frontend");
122
+ watchAndBuild("backend");
123
+ }
124
+
125
+ export { url, html, head, build, isError, HtmlString, type HtmlHandler, Mini };
@@ -0,0 +1,516 @@
1
+ import { htmlResponder, html, json, dangerjson, HtmlString } from "./html";
2
+ import type { DangerJsonInHtml, JsonString, JsonStringValues } from "./html";
3
+ export type Form = {
4
+ post: boolean;
5
+ urlencoded: boolean;
6
+ multipart: boolean;
7
+ formJson?: any;
8
+ formData?: FormData;
9
+ formName?: string;
10
+ hiddenField?: HtmlString;
11
+ actionlink<Y = undefined>(
12
+ qs?: string[] | string,
13
+ settings?: LinkSettings
14
+ ): (mini: Mini<Y>) => string;
15
+ onPostSubmit<F>(cb: () => F): F | undefined;
16
+ };
17
+
18
+ export type DataMaker<X> =
19
+ | ((mini: Mini) => DataMakerReturnType<X>)
20
+ | (() => DataMakerReturnType<X>);
21
+ export type DataMakerReturnType<X> = X | Promise<X>;
22
+ export type HandlerReturnType =
23
+ | JsonString
24
+ | DangerJsonInHtml
25
+ | HtmlString
26
+ | string
27
+ | void;
28
+ export type LazyHandlerReturnType =
29
+ | HandlerReturnType
30
+ | Promise<HandlerReturnType>;
31
+
32
+ export type NamedForm<Z> = {
33
+ formResponse: LazyHandlerReturnType;
34
+ formInfo?: Z;
35
+ };
36
+ export type NamedFormHandlerReturnType<X> =
37
+ | HandlerReturnType
38
+ | Promise<HandlerReturnType>
39
+ | NamedForm<X>
40
+ | Promise<NamedForm<X>>;
41
+
42
+ /**
43
+ * Mini - the data object can be filled with url.data
44
+ * @example
45
+ * ``` js
46
+ * const {html,json, css, data, req, form, link, svg, deliver, route, params, header, head } = mini //pull everything out of the mini handbag
47
+ * ```
48
+ */
49
+ export class Mini<X = undefined> {
50
+ html: typeof html<X>;
51
+ css: typeof html<X>;
52
+ json: typeof json<X>;
53
+ dangerjson: typeof dangerjson<X>;
54
+
55
+ data: X;
56
+ req!: Request;
57
+ head!: (head: HtmlString) => undefined;
58
+ headers!: (headers: HeadersInit, overwrite?: boolean) => undefined;
59
+ options!: (options: ResponseInit) => undefined;
60
+ deliver!: typeof url.deliver;
61
+ route!: string;
62
+ params!: URLSearchParams;
63
+ form!: Form;
64
+
65
+ constructor(mini: Mini<undefined | any>, data: X) {
66
+ Object.assign(this, mini);
67
+ this.html = html<X>;
68
+ this.css = html<X>;
69
+ this.json = json<X>;
70
+ this.dangerjson = dangerjson<X>;
71
+ this.data = data;
72
+ this.deliver = url.deliver;
73
+ this.form.onPostSubmit = (cb) => {
74
+ if (this.form.formName) {
75
+ if (
76
+ this.form.formData &&
77
+ this.form.formData.get("formName") === this.form.formName
78
+ ) {
79
+ return cb();
80
+ } else if (
81
+ this.form.formJson &&
82
+ this.form.formJson.formName === this.form.formName
83
+ ) {
84
+ return cb();
85
+ }
86
+ } else if (this.form.post) {
87
+ return cb();
88
+ }
89
+ };
90
+ }
91
+ }
92
+ /**
93
+ * HtmlHandler
94
+ * @param mini - the mini object
95
+ * @returns - return a partially resolved html string with mini.html
96
+ * @example
97
+ * ``` js
98
+ * const {html,json, css, data, req, form, link, svg, deliver, route, params, header, head } = mini //pull everything out of the mini handbag
99
+ * ```
100
+ */
101
+ export type HtmlHandler<Y = undefined> =
102
+ | ((mini: Mini<Y>) => LazyHandlerReturnType)
103
+ | (() => LazyHandlerReturnType);
104
+ export type NamedFormHandler<Y = undefined, Z = undefined> =
105
+ | ((mini: Mini<Y>) => NamedFormHandlerReturnType<Z>)
106
+ | (() => NamedFormHandlerReturnType<Z>);
107
+
108
+ declare global {
109
+ var FrontendScripts: Array<string>; // An array of the bundled scriptFiles corresponding to the frontend files, example frontends[0] = "index.tsx" -> FrontendScripts[0] = CONTENT OF frontend/index.js
110
+ var FrontendScriptUrls: Array<string>;
111
+ var bundledSVGs: Record<string, { svgContent: string; svgPath: string }>;
112
+ }
113
+ export type ScriptTag = (...params: any[]) => Promise<HtmlString>;
114
+ interface LinkSettings {
115
+ [key: string]: string | null | undefined;
116
+ }
117
+ export class url {
118
+ // direct mapping of "url string" -> function leads to Html Response
119
+ static direct_handlers_html: ReadonlyMap<string, HtmlHandler>;
120
+
121
+ // An array of the uncompiled frontend files, example frontends[0] = "index.tsx" -> frontend/index.tsx (from the project root)
122
+ private static frontends: Array<string> = [];
123
+ private static svgs: Map<string, ResponseInit> = new Map();
124
+
125
+ static svg(
126
+ path: string,
127
+ options: ResponseInit = {
128
+ headers: {
129
+ "Content-Type": "image/svg+xml",
130
+ "Content-Disposition": "attachment",
131
+ },
132
+ }
133
+ ) {
134
+ url.svgs.set(path, options);
135
+ var foundEntry = Object.entries(bundledSVGs).find(
136
+ ([key, value]) => value.svgPath === path
137
+ );
138
+
139
+ return foundEntry && foundEntry[0];
140
+ }
141
+ static frontend(path: string, snippet?: HtmlString) {
142
+ const frontendIndex = url.frontends.push(path) - 1;
143
+ const scriptUrl = FrontendScriptUrls[frontendIndex];
144
+
145
+ return html` ${snippet}
146
+ <script type="module" src="${scriptUrl}"></script>`; // return an html script tag with the index hash
147
+ }
148
+ /**
149
+ * This is used by the frontend bundler in order to find all frontends and their corresponding script files.
150
+ */
151
+ static getFrontends() {
152
+ return url.frontends;
153
+ }
154
+ static getSvgPaths() {
155
+ return [...url.svgs.keys()];
156
+ }
157
+ static serveFrontend(req: Request) {
158
+ const reqPath = new URL(req.url).pathname;
159
+ const index = FrontendScriptUrls.indexOf(reqPath);
160
+
161
+ if (index !== -1) {
162
+ return new Response(FrontendScripts[index], {
163
+ headers: {
164
+ "Content-Type": "application/javascript; charset=utf-8",
165
+ },
166
+ });
167
+ }
168
+ }
169
+ static serveSvg(req: Request) {
170
+ const reqPath = new URL(req.url).pathname;
171
+ const resolvedSvg = bundledSVGs[reqPath];
172
+ if (resolvedSvg) {
173
+ return new Response(
174
+ resolvedSvg.svgContent,
175
+ url.svgs.get(resolvedSvg.svgPath)
176
+ );
177
+ }
178
+ }
179
+ /**
180
+ * tool to expose data to a frontend as a global variable.
181
+ * @param name this will be added as window.name to the window object in the frontend
182
+ * @param value this will be parsed as json in the frontend and asigned as follows: window.name = JSON.parsed(value)
183
+ * @returns the script tag to be embeded in the html response
184
+ *
185
+ * @example
186
+ * ``` js
187
+ * //backend
188
+ * url.deliver("user", userData); // window.user = JSON.parse(userData)
189
+ * //frontend
190
+ * const user = window["user"];
191
+ * ```
192
+ * if you want to use types, declare them like so in your frontend code:
193
+ * ``` ts
194
+ * declare global {
195
+ * var user: string;
196
+ *}
197
+ * ```
198
+ */
199
+ static deliver(name: string, value: JsonStringValues) {
200
+ return html` <script type="application/json" id="${name}">
201
+ ${dangerjson`${value}`}
202
+ </script>
203
+
204
+ <script>
205
+ window["${name}"] = JSON.parse(
206
+ document.getElementById("${name}").innerHTML
207
+ );
208
+ </script>`;
209
+ }
210
+ /**
211
+ * @param dataHandler the function that prepares the data for the handlers
212
+ * @example const {html,json, css, data, req, form, link, svg, deliver, route, params, header, head } = mini //pull everything out of the mini handbag
213
+ * @returns
214
+ */
215
+ static data<T>(dataMaker: DataMaker<T>) {
216
+ return {
217
+ /**
218
+ * @param dataHandler the function that prepares the data for the handlers
219
+ * @example const {html,json, css, data, req, form, link, svg, deliver, route, params, header, head } = mini //pull everything out of the mini handbag
220
+ * @returns
221
+ */
222
+ handler: (dataHandler: HtmlHandler<T>) => {
223
+ return async (oldmini: Mini) => {
224
+ const data = await dataMaker(oldmini);
225
+ const mini = new Mini(oldmini, data);
226
+
227
+ const unresolvedDataHandler = await dataHandler(mini); // passing mini
228
+ if (unresolvedDataHandler instanceof HtmlString) {
229
+ return await unresolvedDataHandler.resolve(mini);
230
+ }
231
+ return unresolvedDataHandler;
232
+ };
233
+ },
234
+ /**
235
+ * use this to **specify the input type for the functions**,
236
+ *
237
+ * that you want to use in the HtmlHandlers that follow this **data blend!**
238
+ * @example type lol = typeof MaybeLoggedIn.$Mini
239
+ */
240
+ $Mini: {
241
+ data: "DONT USE THIS DIRECTLY, ya goofball. This is just to infer the Mini type",
242
+ } as Mini<T>,
243
+ /**
244
+ * use this to **specify the input type for the functions**,
245
+ *
246
+ * that you want to use in the Htmlhandlers that follow this **data blend!**
247
+ * @example type haha = Mini<typeof MaybeLoggedIn.$Data>
248
+ */
249
+ $Data: {
250
+ data: "DONT USE THIS DIRECTLY, ya goofball. This is just to infer the Mini type",
251
+ } as T,
252
+ };
253
+ }
254
+ /**
255
+ * use this to define your routes.
256
+ * @example
257
+ * ``` js
258
+ * url.set([
259
+ * ["/", (mini) => mini.html`<h1>Hello world</h1>`],
260
+ * ["/apple", (mini) => mini.html`<h1>Hello apple</h1>`],
261
+ * ["/banana", (mini) => mini.html`<h1>Hello banana</h1>`],
262
+ * ]);
263
+ * ```
264
+ */
265
+ static set<K extends string>(entries: [K, HtmlHandler][]) {
266
+ url.direct_handlers_html = new Map(entries) as ReadonlyMap<K, HtmlHandler>;
267
+ }
268
+ /**
269
+ * wrap your handlers in this if you mutate something to prevent CSRF issues.
270
+ * @param handler - normal html handler with mini as the argument
271
+ * @returns a wrapped html handler that will only be called when the request is post
272
+ */
273
+ static post(handler: HtmlHandler) {
274
+ return (mini: Mini) => {
275
+ if (mini.form.post) {
276
+ return handler(mini);
277
+ } else {
278
+ return no_post_warning;
279
+ }
280
+ };
281
+ }
282
+ /**
283
+ * wrap your handlers in this if you mutate something to prevent CSRF issues.
284
+ * @param handler - normal html handler with mini as the argument
285
+ * @returns a wrapped html handler that will only be called when the request is post and contains a json body
286
+ */
287
+ static postJson(handler: HtmlHandler) {
288
+ return (mini: Mini) => {
289
+ if (mini.form.formJson) {
290
+ return handler(mini);
291
+ } else {
292
+ return no_post_warning;
293
+ }
294
+ };
295
+ }
296
+ /**
297
+ * wrap your handlers in this if you mutate something to prevent CSRF issues.
298
+ * @param handler - normal html handler with mini as the argument
299
+ * @returns a wrapped html handler that will only be called when the request is post and contains a FormData body
300
+ */
301
+ static postFormData(handler: HtmlHandler) {
302
+ return (mini: Mini) => {
303
+ if (mini.form.formData) {
304
+ return handler(mini);
305
+ } else {
306
+ return no_post_warning;
307
+ }
308
+ };
309
+ }
310
+ /**
311
+ * This is useful to decouple forms from routes.
312
+ * @param name name of the form - mini.form.onPostSubmit() will only be called if a (possibly hidden) field called formName matches this
313
+ * @param handler just like a normal handler (aka you can return the form as a HtmlString), but you can optionally return additional data in formInfo
314
+ * @returns - { formResponse: result of the handler, formInfo?: some info about the form. Totally up to you}
315
+ */
316
+ static namedForm<X = undefined, Z = undefined>(
317
+ name: string,
318
+ handler: NamedFormHandler<X, Z>
319
+ ) {
320
+ return async (mini: Mini<X>) => {
321
+ mini.form.formName = name;
322
+ mini.form.hiddenField = html`<input
323
+ type="hidden"
324
+ name="formName"
325
+ value="${name}"
326
+ />`;
327
+ const namedFormResponse = await handler(mini);
328
+ let handlerResult = {} as NamedForm<Z>;
329
+ if (
330
+ typeof namedFormResponse !== "string" &&
331
+ namedFormResponse &&
332
+ "formResponse" in namedFormResponse
333
+ ) {
334
+ handlerResult.formResponse = await namedFormResponse.formResponse;
335
+ handlerResult.formInfo = namedFormResponse.formInfo;
336
+ } else {
337
+ handlerResult.formResponse = namedFormResponse;
338
+ }
339
+ delete mini.form.formName;
340
+ delete mini.form.hiddenField;
341
+ return handlerResult;
342
+ };
343
+ }
344
+
345
+ /**
346
+ * pass in all the query string parameter names that you want to preserve in the link
347
+ * @param Url - the url that you want to link to (example: "/login")
348
+ * @param qs - the query string parameters that you want to preserve in the link
349
+ * @param settings - key and string values that you want to set in the link
350
+ * @returns - the link that you can use in your html template
351
+ */
352
+ static link<X>(
353
+ Url: string,
354
+ qs: string[] | string = "",
355
+ settings?: LinkSettings
356
+ ) {
357
+ return (mini: Mini<X>) => {
358
+ return url.currylink(Url, qs, mini.req, settings);
359
+ };
360
+ }
361
+ static currylink(
362
+ Url: string,
363
+ qs: string[] | string,
364
+ req: Request,
365
+ settings?: LinkSettings
366
+ ) {
367
+ if (!Array.isArray(qs)) {
368
+ qs = [qs];
369
+ }
370
+ // Create a new URL object from the current location
371
+ // https://github.com/whatwg/url/issues/531#issuecomment-1337050285
372
+ const GOOFY_HACK = "http://goofyhack.com";
373
+ const updatedUrl = new URL(url.get(Url), GOOFY_HACK);
374
+ for (const q of qs) {
375
+ // Use URLSearchParams to set the name query parameter
376
+ const reqParam = new URL(req.url).searchParams.get(q);
377
+ if (reqParam) {
378
+ updatedUrl.searchParams.set(q, reqParam);
379
+ }
380
+ }
381
+ for (const key in settings) {
382
+ const value = settings[key];
383
+ if (value !== undefined && value !== null) {
384
+ updatedUrl.searchParams.set(key, value);
385
+ }
386
+ }
387
+ // Return the updated URL as a string
388
+ return updatedUrl.toString().slice(GOOFY_HACK.length);
389
+ }
390
+ /**
391
+ * This method retrieves a url from the urls array. If the url does not exist in the urls array, an error will be thrown.
392
+ * @param {string} Url - The url to retrieve.
393
+ * @return {string} - The retrieved url.
394
+ * @throws Will throw an Error if the provided url is not found in the urls array.
395
+ */
396
+ static get(Url: string): string {
397
+ const foundUrl = url.direct_handlers_html.get(Url);
398
+ if (!foundUrl) {
399
+ throw new Error(`URL "${html`${Url}`}" was not set.`);
400
+ }
401
+ return Url;
402
+ }
403
+ static async match(req: Request, reqPath?: string) {
404
+ if (typeof reqPath === "undefined") {
405
+ reqPath = new URL(req.url).pathname;
406
+ }
407
+ const handler = url.direct_handlers_html.get(reqPath);
408
+ if (handler) {
409
+ //this is the source of mini
410
+ let handlerHead: HtmlString | undefined = undefined;
411
+ let handlerOptions: ResponseInit = {
412
+ headers: {
413
+ "Content-Type": "text/html; charset=utf-8",
414
+ },
415
+ };
416
+ const post = req.method === "POST";
417
+ let formJson: any;
418
+ let formData: FormData | undefined;
419
+ const urlencoded = (req.headers.get("Content-Type") + "").includes(
420
+ "application/x-www-form-urlencoded"
421
+ );
422
+ const multipart = (req.headers.get("Content-Type") + "").includes(
423
+ "multipart/form-data"
424
+ );
425
+ if (post && !urlencoded && !multipart) {
426
+ formJson = await req.json();
427
+ }
428
+ if (post && (urlencoded || multipart)) {
429
+ formData = await req.formData();
430
+ }
431
+
432
+ const mini = new Mini(
433
+ {
434
+ data: undefined,
435
+ req,
436
+ html,
437
+ css: html,
438
+ deliver: url.deliver,
439
+ route: reqPath,
440
+ params: new URL(req.url).searchParams,
441
+ json,
442
+ form: {
443
+ post,
444
+ urlencoded,
445
+ multipart,
446
+ formJson,
447
+ formData,
448
+ onPostSubmit(cb) {
449
+ if (post) {
450
+ return cb();
451
+ }
452
+ },
453
+ actionlink: (qs = "", settings) => url.link(reqPath, qs, settings),
454
+ },
455
+ dangerjson,
456
+ head: (head) => {
457
+ handlerHead = head;
458
+ },
459
+ headers: (headers, overwrite = false) => {
460
+ if (overwrite) {
461
+ handlerOptions.headers = headers;
462
+ } else {
463
+ handlerOptions.headers = {
464
+ ...handlerOptions.headers,
465
+ ...headers,
466
+ };
467
+ }
468
+ },
469
+ options: (options) => {
470
+ handlerOptions = options;
471
+ },
472
+ },
473
+ undefined
474
+ );
475
+ const unresolved = await handler(mini); //passing mini
476
+ return htmlResponder(mini, unresolved, handlerHead, handlerOptions);
477
+ }
478
+ }
479
+
480
+ /**
481
+ * Fetch handler that is called by the server when a request is made to any of the urls.
482
+ * @param {Request} req - The Request object.
483
+ * @return {Promise<Response>} - The Response object.
484
+ */
485
+ static async install(req: Request) {
486
+ //go through all the Htmlhandlers and see if there is a match
487
+ let res = await url.match(req);
488
+ if (res) return res;
489
+
490
+ //handle frontend js file serving
491
+ res = url.serveFrontend(req);
492
+ if (res) return res;
493
+ //handle svg file serving
494
+ res = url.serveSvg(req);
495
+ if (res) return res;
496
+ // go through all the Htmlhandlers again with added slash at the end.
497
+ res = await url.match(req, new URL(req.url).pathname + "/");
498
+ if (res) return res;
499
+
500
+ return new Response("No matching url found", { status: 404 });
501
+ }
502
+ }
503
+
504
+ const no_post_warning = html`<div style="color:red;">
505
+ This method is only accessible through the POST method. Remember to make all
506
+ mutations (insert / update data in the database) only accessible via POST and
507
+ implement your session cookies like this:
508
+ <div
509
+ style="color:#0FFF50; width:800px; overflow:wrap; margin-left:30px; margin-top:20px; margin-bottom:20px;"
510
+ >
511
+ "Set-Cookie": sessionId=="some random string made with crypto.randomUUID()"
512
+ expires=Thu, 01 Jan 1970 00:00:00 GMT Secure; HttpOnly; SameSite=Strict;
513
+ path=/,
514
+ </div>
515
+ This is necessary to prevent CSRF issues.
516
+ </div>`;
package/package.json CHANGED
@@ -1,8 +1,14 @@
1
1
  {
2
2
  "name": "@spirobel/mininext",
3
- "module": "index.ts",
3
+ "module": "dist/mininext.js",
4
4
  "type": "module",
5
- "version": "0.2.0",
5
+ "main": "dist/mininext.js",
6
+ "types": "dist/mininext.d.ts",
7
+ "scripts": {
8
+ "build": "tsc"
9
+ },
10
+ "files": ["dist", "mininext"],
11
+ "version": "0.2.2",
6
12
  "devDependencies": {
7
13
  "@types/bun": "latest"
8
14
  },
package/bun.lockb DELETED
Binary file
package/index.ts DELETED
@@ -1 +0,0 @@
1
- console.log("Hello via Bun!");
package/tsconfig.json DELETED
@@ -1,27 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- // Enable latest features
4
- "lib": ["ESNext", "DOM"],
5
- "target": "ESNext",
6
- "module": "ESNext",
7
- "moduleDetection": "force",
8
- "jsx": "react-jsx",
9
- "allowJs": true,
10
-
11
- // Bundler mode
12
- "moduleResolution": "bundler",
13
- "allowImportingTsExtensions": true,
14
- "verbatimModuleSyntax": true,
15
- "noEmit": true,
16
-
17
- // Best practices
18
- "strict": true,
19
- "skipLibCheck": true,
20
- "noFallthroughCasesInSwitch": true,
21
-
22
- // Some stricter flags (disabled by default)
23
- "noUnusedLocals": false,
24
- "noUnusedParameters": false,
25
- "noPropertyAccessFromIndexSignature": false
26
- }
27
- }