@hyperspan/framework 0.0.1 → 0.0.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.
package/build.ts ADDED
@@ -0,0 +1,29 @@
1
+ import dts from 'bun-plugin-dts';
2
+
3
+ await Promise.all([
4
+ // Build JS
5
+ Bun.build({
6
+ entrypoints: ['./src/index.ts'],
7
+ outdir: './dist',
8
+ target: 'browser',
9
+ }),
10
+ Bun.build({
11
+ entrypoints: ['./src/server.ts'],
12
+ outdir: './dist',
13
+ target: 'node',
14
+ }),
15
+
16
+ // Build type files for TypeScript
17
+ Bun.build({
18
+ entrypoints: ['./src/index.ts'],
19
+ outdir: './dist',
20
+ target: 'browser',
21
+ plugins: [dts()],
22
+ }),
23
+ Bun.build({
24
+ entrypoints: ['./src/server.ts'],
25
+ outdir: './dist',
26
+ target: 'node',
27
+ plugins: [dts()],
28
+ }),
29
+ ]);
package/bun.lockb CHANGED
Binary file
@@ -0,0 +1,50 @@
1
+ // Generated by dts-bundle-generator v9.5.1
2
+
3
+ /**
4
+ * Template object - used so it will be possible to (eventually) pass down context
5
+ */
6
+ export declare class HSTemplate {
7
+ __hsTemplate: boolean;
8
+ content: any[];
9
+ constructor(content: any[]);
10
+ }
11
+ /**
12
+ * HTML template
13
+ */
14
+ export declare function html(strings: TemplateStringsArray, ...values: any[]): HSTemplate;
15
+ export declare namespace html {
16
+ var raw: (value: string) => HSTemplate;
17
+ }
18
+ /**
19
+ * Render HSTemplate to async generator that streams output to a string
20
+ */
21
+ export declare function renderToStream(template: HSTemplate | string): AsyncGenerator<string>;
22
+ /**
23
+ * Render HSTemplate to string (awaits/buffers entire response)
24
+ */
25
+ export declare function renderToString(template: HSTemplate | string): Promise<string>;
26
+ /**
27
+ * Strip extra spacing between HTML tags (used for tests)
28
+ */
29
+ export declare function compressHTMLString(str: string): string;
30
+ /**
31
+ * LOL JavaScript...
32
+ */
33
+ export declare function _typeOf(obj: any): string;
34
+ /**
35
+ * Client component
36
+ */
37
+ export type THSWCState = Record<string, any>;
38
+ export type THSWCSetStateArg = THSWCState | ((state: THSWCState) => THSWCState);
39
+ export type THSWC = {
40
+ this: THSWC;
41
+ state: THSWCState | undefined;
42
+ id: string;
43
+ setState: (fn: THSWCSetStateArg) => THSWCState;
44
+ mergeState: (newState: THSWCState) => THSWCState;
45
+ render: () => any;
46
+ };
47
+ export type THSWCUser = Pick<THSWC, "render"> & Record<string, any>;
48
+ export declare function clientComponent(id: string, wc: THSWCUser): (attrs?: Record<string, string>, state?: Record<string, any>) => HSTemplate;
49
+
50
+ export {};
@@ -0,0 +1,109 @@
1
+ // Generated by dts-bundle-generator v9.5.1
2
+
3
+ import Headers$1 from '@mjackson/headers';
4
+
5
+ declare class HSTemplate {
6
+ __hsTemplate: boolean;
7
+ content: any[];
8
+ constructor(content: any[]);
9
+ }
10
+ export type THTTPMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
11
+ declare class HSRequestContext {
12
+ req: Request;
13
+ locals: Record<string, any>;
14
+ headers: Headers$1;
15
+ route: {
16
+ params: Record<string, string>;
17
+ query: URLSearchParams;
18
+ };
19
+ constructor(req: Request, params?: Record<string, string>);
20
+ /**
21
+ * Response helper
22
+ * Merges a Response object while preserving all headers added in context/middleware
23
+ */
24
+ resMerge(res: Response): Response;
25
+ /**
26
+ * HTML response helper
27
+ * Preserves all headers added in context/middleware
28
+ */
29
+ html(content: string, options?: ResponseInit): Response;
30
+ /**
31
+ * JSON response helper
32
+ * Preserves all headers added in context/middleware
33
+ */
34
+ json(content: any, options?: ResponseInit): Response;
35
+ notFound(msg?: string): Response;
36
+ }
37
+ export type THSRouteHandler = (context: HSRequestContext) => (Response | null | void) | Promise<Response | null | void>;
38
+ declare class HSApp {
39
+ private _router;
40
+ private _mw;
41
+ _defaultRoute: THSRouteHandler;
42
+ constructor();
43
+ get(path: string, handler: THSRouteHandler): this;
44
+ post(path: string, handler: THSRouteHandler): this;
45
+ put(path: string, handler: THSRouteHandler): this;
46
+ delete(path: string, handler: THSRouteHandler): this;
47
+ all(path: string, handler: THSRouteHandler): this;
48
+ addRoute(methods: THTTPMethod[], path: string, handler: THSRouteHandler): this;
49
+ defaultRoute(handler: THSRouteHandler): void;
50
+ private _route;
51
+ run(req: Request): Promise<Response>;
52
+ }
53
+ export declare const IS_PROD: boolean;
54
+ /**
55
+ * Run route from file
56
+ */
57
+ export declare function runFileRoute(routeFile: string, context: HSRequestContext): Promise<any>;
58
+ export type THSServerConfig = {
59
+ appDir: string;
60
+ staticFileRoot: string;
61
+ beforeRoutesAdded?: (app: HSApp) => void;
62
+ afterRoutesAdded?: (app: HSApp) => void;
63
+ };
64
+ export type THSRouteMap = {
65
+ file: string;
66
+ route: string;
67
+ params: string[];
68
+ };
69
+ export declare function buildRoutes(config: THSServerConfig): Promise<THSRouteMap[]>;
70
+ /**
71
+ * Create and start Bun HTTP server
72
+ */
73
+ export declare function createServer(config: THSServerConfig): Promise<HSApp>;
74
+ /**
75
+ * Build client JS for end users (minimal JS for Hyperspan to work)
76
+ */
77
+ export declare let clientJSFile: string;
78
+ export declare function buildClientJS(): Promise<string>;
79
+ /**
80
+ * Find client CSS file built for end users
81
+ * @TODO: Build this in code here vs. relying on tailwindcss CLI tool from package scripts
82
+ */
83
+ export declare let clientCSSFile: string;
84
+ export declare function buildClientCSS(): Promise<string | undefined>;
85
+ /**
86
+ * Streaming HTML Response
87
+ */
88
+ export declare class StreamResponse {
89
+ constructor(iterator: AsyncIterator<unknown>, options?: {});
90
+ }
91
+ /**
92
+ * Does what it says on the tin...
93
+ */
94
+ export declare function createReadableStreamFromAsyncGenerator(output: AsyncGenerator): ReadableStream<any>;
95
+ /**
96
+ * Form route
97
+ * Automatically handles and parses form data
98
+ *
99
+ * 1. Renders component as initial form markup
100
+ * 2. Bind form onSubmit function to custom client JS handling
101
+ * 3. Submits form with JavaScript fetch()
102
+ * 4. Replaces form content with content from server
103
+ * 5. All validation and save logic is on the server
104
+ * 6. Handles any Exception thrown on server as error displayed in client
105
+ */
106
+ export type TFormRouteFn = (context: HSRequestContext) => HSTemplate | Response;
107
+ export declare function formRoute(handlerFn: TFormRouteFn): (context: HSRequestContext) => void;
108
+
109
+ export {};
package/dist/server.js CHANGED
@@ -1,4 +1,3 @@
1
- // @bun
2
1
  var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getProtoOf = Object.getPrototypeOf;
@@ -412,8 +411,8 @@ var require_deepmerge = __commonJS((exports, module) => {
412
411
  });
413
412
 
414
413
  // src/server.ts
415
- import {readdir} from "fs/promises";
416
- import {basename, extname, join} from "path";
414
+ import {readdir} from "node:fs/promises";
415
+ import {basename, extname, join, resolve} from "node:path";
417
416
 
418
417
  // src/html.ts
419
418
  var import_escape_html = __toESM(require_escape_html(), 1);
@@ -1823,11 +1822,12 @@ async function createServer(config) {
1823
1822
  });
1824
1823
  config.beforeRoutesAdded && config.beforeRoutesAdded(app2);
1825
1824
  const fileRoutes = await buildRoutes(config);
1825
+ const routeMap = [];
1826
1826
  for (let i = 0;i < fileRoutes.length; i++) {
1827
1827
  let route = fileRoutes[i];
1828
1828
  const fullRouteFile = join("../../", config.appDir, "routes", route.file);
1829
1829
  const routePattern = normalizePath(route.route);
1830
- console.log("[Hyperspan] Added route: ", routePattern);
1830
+ routeMap.push({ route: routePattern, file: config.appDir + "/routes/" + route.file });
1831
1831
  app2.all(routePattern, async (context) => {
1832
1832
  const matchedRoute = await runFileRoute(fullRouteFile, context);
1833
1833
  if (matchedRoute) {
@@ -1836,6 +1836,10 @@ async function createServer(config) {
1836
1836
  return app2._defaultRoute(context);
1837
1837
  });
1838
1838
  }
1839
+ if (!IS_PROD) {
1840
+ console.log("[Hyperspan] File system routes (in app/routes):");
1841
+ console.table(routeMap);
1842
+ }
1839
1843
  config.afterRoutesAdded && config.afterRoutesAdded(app2);
1840
1844
  app2.all("*", (context) => {
1841
1845
  const req = context.req;
@@ -1855,8 +1859,9 @@ async function createServer(config) {
1855
1859
  return app2;
1856
1860
  }
1857
1861
  async function buildClientJS() {
1862
+ const sourceFile = resolve(CWD, "../", "./src/clientjs/hyperspan-client.ts");
1858
1863
  const output = await Bun.build({
1859
- entrypoints: ["./src/hyperspan/clientjs/hyperspan-client.ts"],
1864
+ entrypoints: [sourceFile],
1860
1865
  outdir: `./public/_hs/js`,
1861
1866
  naming: IS_PROD ? "[dir]/[name]-[hash].[ext]" : undefined,
1862
1867
  minify: IS_PROD
@@ -1900,6 +1905,7 @@ function formRoute(handlerFn) {
1900
1905
  };
1901
1906
  }
1902
1907
  var IS_PROD = false;
1908
+ var CWD = import.meta.dir;
1903
1909
  var STATIC_FILE_MATCHER = /[^/\\&\?]+\.([a-zA-Z]+)$/;
1904
1910
  var _routeCache = {};
1905
1911
  var ROUTE_SEGMENT = /(\[[a-zA-Z_\.]+\])/g;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperspan/framework",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Hyperspan Web Framework",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -9,9 +9,7 @@
9
9
  "access": "public"
10
10
  },
11
11
  "scripts": {
12
- "build": "bun run clean && bun run build:client && bun run build:server",
13
- "build:client": "bun build ./src/index.ts --outdir ./dist --target browser",
14
- "build:server": "bun build ./src/server.ts --outdir ./dist --target bun",
12
+ "build": "bun ./build.ts",
15
13
  "clean": "rm -rf dist",
16
14
  "test": "bun test",
17
15
  "prepack": "npm run clean && npm run build"
@@ -20,7 +18,15 @@
20
18
  "type": "git",
21
19
  "url": "https://github.com/vlucas/hyperspan-framework"
22
20
  },
23
- "keywords": ["framework", "node", "bun", "web", "framework", "javascript", "typescript"],
21
+ "keywords": [
22
+ "framework",
23
+ "node",
24
+ "bun",
25
+ "web",
26
+ "framework",
27
+ "javascript",
28
+ "typescript"
29
+ ],
24
30
  "author": "Vance Lucas <vance@vancelucas.com>",
25
31
  "license": "BSD-3-Clause",
26
32
  "bugs": {
@@ -34,5 +40,12 @@
34
40
  "isbot": "^5.1.17",
35
41
  "trek-middleware": "^1.2.0",
36
42
  "trek-router": "^1.2.0"
43
+ },
44
+ "devDependencies": {
45
+ "@types/bun": "^1.1.9",
46
+ "@types/escape-html": "^1.0.4",
47
+ "@types/node": "^22.5.5",
48
+ "bun-plugin-dts": "^0.2.3",
49
+ "typescript": "^5.6.2"
37
50
  }
38
51
  }
package/src/server.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  import { readdir } from 'node:fs/promises';
2
- import { basename, extname, join } from 'node:path';
2
+ import { basename, extname, join, resolve } from 'node:path';
3
3
  import { html, renderToStream, renderToString } from './html';
4
4
  import { isbot } from 'isbot';
5
5
  import { HSTemplate } from './html';
6
6
  import { HSApp, HSRequestContext, normalizePath } from './app';
7
7
 
8
8
  export const IS_PROD = process.env.NODE_ENV === 'production';
9
+ const CWD = import.meta.dir;
9
10
  const STATIC_FILE_MATCHER = /[^/\\&\?]+\.([a-zA-Z]+)$/;
10
11
 
11
12
  // Cached route components
@@ -215,14 +216,14 @@ export async function createServer(config: THSServerConfig): Promise<HSApp> {
215
216
 
216
217
  // Scan routes folder and add all file routes to the router
217
218
  const fileRoutes = await buildRoutes(config);
219
+ const routeMap = [];
218
220
 
219
221
  for (let i = 0; i < fileRoutes.length; i++) {
220
222
  let route = fileRoutes[i];
221
223
  const fullRouteFile = join('../../', config.appDir, 'routes', route.file);
222
224
  const routePattern = normalizePath(route.route);
223
225
 
224
- // @ts-ignore
225
- console.log('[Hyperspan] Added route: ', routePattern);
226
+ routeMap.push({ route: routePattern, file: config.appDir + '/routes/' + route.file });
226
227
 
227
228
  app.all(routePattern, async (context) => {
228
229
  const matchedRoute = await runFileRoute(fullRouteFile, context);
@@ -234,6 +235,12 @@ export async function createServer(config: THSServerConfig): Promise<HSApp> {
234
235
  });
235
236
  }
236
237
 
238
+ // Display routing table for dev env
239
+ if (!IS_PROD) {
240
+ console.log('[Hyperspan] File system routes (in app/routes):');
241
+ console.table(routeMap);
242
+ }
243
+
237
244
  // [Customization] After routes added...
238
245
  config.afterRoutesAdded && config.afterRoutesAdded(app);
239
246
 
@@ -267,8 +274,9 @@ export async function createServer(config: THSServerConfig): Promise<HSApp> {
267
274
  */
268
275
  export let clientJSFile: string;
269
276
  export async function buildClientJS() {
277
+ const sourceFile = resolve(CWD, '../', './src/clientjs/hyperspan-client.ts');
270
278
  const output = await Bun.build({
271
- entrypoints: ['./src/hyperspan/clientjs/hyperspan-client.ts'],
279
+ entrypoints: [sourceFile],
272
280
  outdir: `./public/_hs/js`,
273
281
  naming: IS_PROD ? '[dir]/[name]-[hash].[ext]' : undefined,
274
282
  minify: IS_PROD,
package/tsconfig.json CHANGED
@@ -19,8 +19,7 @@
19
19
  "skipLibCheck": true,
20
20
  "allowSyntheticDefaultImports": true,
21
21
  "forceConsistentCasingInFileNames": true,
22
- "allowJs": true,
23
- "types": ["bun-types"]
22
+ "allowJs": true
24
23
  },
25
24
  "exclude": ["node_modules", "__tests__", "*.test.ts"]
26
25
  }