@varlabs/create-solidstep 0.1.7 → 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.
@@ -0,0 +1,33 @@
1
+ import { defineInstrumentation } from 'solidstep/utils/instrumentation';
2
+
3
+ export default defineInstrumentation({
4
+ async register() {
5
+ // Called once at server startup.
6
+ // Initialize your telemetry SDK here (e.g., OpenTelemetry, Sentry).
7
+ console.log('[instrumentation] Server starting...');
8
+ },
9
+
10
+ async onRequest(request, context) {
11
+ // Called before each request is processed.
12
+ // context.metadata is a mutable object you can use to pass data between hooks.
13
+ context.metadata.requestId = crypto.randomUUID();
14
+ console.log(
15
+ `[instrumentation] ${request.method} ${context.pathname} (${context.routeType})`,
16
+ );
17
+ },
18
+
19
+ async onResponseEnd(request, context) {
20
+ // Called after the response stream is complete.
21
+ console.log(
22
+ `[instrumentation] ${context.statusCode} ${context.pathname} ${context.duration.toFixed(1)}ms`,
23
+ );
24
+ },
25
+
26
+ async onRequestError(error, request, context) {
27
+ // Called when an unhandled error occurs during request processing.
28
+ console.error(
29
+ `[instrumentation] Error in ${context.pathname}:`,
30
+ error.message,
31
+ );
32
+ },
33
+ });
@@ -1,39 +1,37 @@
1
- import type { Component, JSX } from 'solid-js';
2
- import './globals.css';
3
-
4
- export const generateMeta = ({ cspNonce }) => ({
5
- 'title': {
6
- type: 'title',
7
- attributes: {},
8
- content: 'SolidStep App'
9
- },
10
- 'description': {
11
- type: 'meta',
12
- attributes: {
13
- name: 'description',
14
- content: 'This is simple SolidStep application.'
15
- },
16
- },
17
- 'favicon': {
18
- type: 'link',
19
- attributes: {
20
- rel: 'icon',
21
- href: '/favicon-32x32.png',
22
- type: 'image/png'
23
- }
24
- },
25
- });
26
-
27
- const Layout: Component<{
28
- children: JSX.Element;
29
- }> = ({
30
- children,
31
- }) => {
32
- return (
33
- <body>
34
- {children}
35
- </body>
36
- );
37
- }
38
-
39
- export default Layout;
1
+ import type { Component, JSX } from 'solid-js';
2
+ import './globals.css';
3
+
4
+ export const generateMeta = () => ({
5
+ 'title': {
6
+ type: 'title',
7
+ attributes: {},
8
+ content: 'SolidStep App'
9
+ },
10
+ 'description': {
11
+ type: 'meta',
12
+ attributes: {
13
+ name: 'description',
14
+ content: 'This is a simple SolidStep application.'
15
+ },
16
+ },
17
+ 'favicon': {
18
+ type: 'link',
19
+ attributes: {
20
+ rel: 'icon',
21
+ href: '/favicon-32x32.png',
22
+ type: 'image/png'
23
+ }
24
+ },
25
+ });
26
+
27
+ const Layout: Component<{
28
+ children: () => JSX.Element;
29
+ }> = (props) => {
30
+ return (
31
+ <body>
32
+ {props.children()}
33
+ </body>
34
+ );
35
+ };
36
+
37
+ export default Layout;
@@ -1,60 +1,66 @@
1
- import { defineMiddleware } from 'vinxi/http';
2
- import { createBasePolicy, serializePolicy, withNonce } from 'solidstep/utils/csp';
3
- import { cors } from 'solidstep/utils/cors';
4
- import { csrf } from 'solidstep/utils/csrf';
5
- import { randomBytes } from 'node:crypto';
6
-
7
- const trustedOrigins = ['https://example.com', 'https://another-example.com'];
8
-
9
- const corsMiddleware = cors(trustedOrigins);
10
- const csrfMiddleware = csrf(trustedOrigins);
11
- let cspPolicy = createBasePolicy();
12
-
13
- const middleware = defineMiddleware({
14
- onRequest: async (event) => {
15
- const nonce = randomBytes(16).toString('base64');
16
-
17
- (event as any).locals = {
18
- cspNonce: nonce,
19
- };
20
-
21
- cspPolicy = withNonce(cspPolicy, nonce);
22
-
23
- event.node.res.setHeader('Content-Security-Policy', serializePolicy(cspPolicy));
24
- event.node.res.setHeader('Vary', 'Origin, Access-Control-Request-Method');
25
-
26
- const origin = event.node.req.headers.origin || '';
27
- const protocol = origin.startsWith('https') ? 'https' : 'http';
28
- const requestUrl = new URL(event.node.req.url, `${protocol}://${event.node.req.headers.host || 'localhost'}`);
29
-
30
- const csrfResult = csrfMiddleware(
31
- event.node.req.method,
32
- requestUrl,
33
- origin,
34
- event.node.req.headers.referer
35
- );
36
-
37
- if (!csrfResult.success) {
38
- event.node.res.statusCode = 403; // Forbidden
39
- event.node.res.end(csrfResult.message);
40
- return;
41
- }
42
-
43
- if (origin) {
44
- const corsHeaders = corsMiddleware(origin, event.node.req.method === 'OPTIONS');
45
- for (const [key, value] of Object.entries(corsHeaders)) {
46
- event.node.res.setHeader(key, value);
47
- }
48
- if (
49
- event.node.req.method === 'OPTIONS'
50
- && event.node.req.headers['access-control-request-method']
51
- ) {
52
- event.node.res.statusCode = 204; // No Content for preflight requests
53
- event.node.res.end();
54
- return;
55
- }
56
- }
57
- },
58
- });
59
-
60
- export default middleware;
1
+ import { defineMiddleware, type Middleware } from 'solidstep/utils/middleware';
2
+ import { createBasePolicy, serializePolicy, withNonce } from 'solidstep/utils/csp';
3
+ import { cors } from 'solidstep/utils/cors';
4
+ import { csrf } from 'solidstep/utils/csrf';
5
+ import { randomBytes } from 'node:crypto';
6
+
7
+ const trustedOrigins = ['https://example.com', 'https://another-example.com'];
8
+
9
+ const corsMiddleware = cors(trustedOrigins);
10
+ const csrfMiddleware = csrf(trustedOrigins);
11
+
12
+ // Logs every request. A lightweight composable unit.
13
+ const logger: Middleware = {
14
+ onRequest: (event) => {
15
+ console.log(`[req] ${event.node.req.method} ${event.path}`);
16
+ },
17
+ };
18
+
19
+ // Sets a per-request CSP nonce, enforces CSRF on unsafe methods, and applies
20
+ // CORS for trusted origins. Short-circuits the chain by returning a Response.
21
+ const security: Middleware = {
22
+ onRequest: (event) => {
23
+ const nonce = randomBytes(16).toString('base64');
24
+ (event as any).locals = { cspNonce: nonce };
25
+
26
+ let policy = createBasePolicy();
27
+ policy = withNonce(policy, nonce);
28
+ event.node.res.setHeader('Content-Security-Policy', serializePolicy(policy));
29
+ event.node.res.setHeader('Vary', 'Origin, Access-Control-Request-Method');
30
+
31
+ const origin = (event.node.req.headers.origin as string) || '';
32
+ const protocol = origin.startsWith('https') ? 'https' : 'http';
33
+ const requestUrl = new URL(
34
+ event.node.req.url || '/',
35
+ `${protocol}://${event.node.req.headers.host || 'localhost'}`,
36
+ );
37
+
38
+ const csrfResult = csrfMiddleware(
39
+ event.node.req.method || 'GET',
40
+ requestUrl,
41
+ origin,
42
+ event.node.req.headers.referer,
43
+ );
44
+ if (!csrfResult.success) {
45
+ return new Response(csrfResult.message, { status: 403 });
46
+ }
47
+
48
+ if (origin) {
49
+ const corsHeaders = corsMiddleware(
50
+ origin,
51
+ event.node.req.method === 'OPTIONS',
52
+ );
53
+ for (const [key, value] of Object.entries(corsHeaders)) {
54
+ event.node.res.setHeader(key, value);
55
+ }
56
+ if (
57
+ event.node.req.method === 'OPTIONS' &&
58
+ event.node.req.headers['access-control-request-method']
59
+ ) {
60
+ return new Response(null, { status: 204 });
61
+ }
62
+ }
63
+ },
64
+ };
65
+
66
+ export default defineMiddleware([logger, security]);
@@ -1,40 +1,37 @@
1
- import { defineLoader, type LoaderDataFromFunction } from 'solidstep/utils/loader';
2
- import { NoHydration } from 'solid-js/web';
3
-
4
- export const loader = defineLoader(async () => {
5
- const response = await fetch('https://jsonplaceholder.typicode.com/todos/2');
6
- if (!response.ok) {
7
- throw new Error('Failed to fetch data');
8
- }
9
- console.log('Fetching data from API...');
10
- const data = await response.json() as Promise<{ userId: number; id: number; title: string; completed: boolean }>;
11
- return data;
12
- });
13
-
14
- export const generateMeta = () => ({
15
- 'title': {
16
- type: 'title',
17
- attributes: {},
18
- content: 'SolidStep Example Main Page'
19
- },
20
- });
21
-
22
- type LoaderData = LoaderDataFromFunction<typeof loader>;
23
-
24
- const Page = ({
25
- loaderData
26
- }: {
27
- loaderData: LoaderData;
28
- }) => {
29
- return (
30
- <div class="flex flex-col items-center justify-center min-h-screen bg-gray-100">
31
- <NoHydration>
32
- <p class="text-lg text-gray-700">ID: {loaderData.id}</p>
33
- </NoHydration>
34
- <h1 class="text-4xl font-bold mb-4">Welcome to My App</h1>
35
- <p class="text-lg text-gray-700">This is a simple Next.js application.</p>
36
- </div>
37
- );
38
- };
39
-
40
- export default Page;
1
+ import { defineLoader, type LoaderDataFromFunction } from 'solidstep/utils/loader';
2
+
3
+ export const loader = defineLoader(async () => {
4
+ const response = await fetch('https://jsonplaceholder.typicode.com/todos/2');
5
+ if (!response.ok) {
6
+ throw new Error('Failed to fetch data');
7
+ }
8
+ const data = (await response.json()) as {
9
+ userId: number;
10
+ id: number;
11
+ title: string;
12
+ completed: boolean;
13
+ };
14
+ return data;
15
+ });
16
+
17
+ export const generateMeta = () => ({
18
+ 'title': {
19
+ type: 'title',
20
+ attributes: {},
21
+ content: 'SolidStep Main Page'
22
+ },
23
+ });
24
+
25
+ type LoaderData = LoaderDataFromFunction<typeof loader>;
26
+
27
+ const Page = (props: { loaderData: LoaderData }) => {
28
+ return (
29
+ <main>
30
+ <h1>Welcome to SolidStep</h1>
31
+ <p>Edit <code>app/page.tsx</code> to get started.</p>
32
+ <p>Loaded todo #{props.loaderData.id}: {props.loaderData.title}</p>
33
+ </main>
34
+ );
35
+ };
36
+
37
+ export default Page;
@@ -1,3 +1,18 @@
1
- import { defineConfig } from 'solidstep';
2
-
3
- export default defineConfig({});
1
+ import { defineConfig } from 'solidstep';
2
+ import { fileURLToPath } from 'node:url';
3
+
4
+ // Nitro's default Node database connector bundles an unused `import 'node:sqlite'`,
5
+ // a builtin that only exists in Node 22.5+. This starter uses no database, so we both
6
+ // pass an empty `database` config and alias the builtin to an empty stub, keeping the
7
+ // production server runnable on Node 20/21.
8
+ const sqliteStub = fileURLToPath(new URL('./sqlite-stub.mjs', import.meta.url));
9
+
10
+ export default defineConfig({
11
+ server: {
12
+ preset: 'node-server',
13
+ database: {},
14
+ alias: {
15
+ 'node:sqlite': sqliteStub,
16
+ },
17
+ },
18
+ });
@@ -12,7 +12,7 @@
12
12
  "author": "",
13
13
  "dependencies": {
14
14
  "solid-js": "^1.9.9",
15
- "solidstep": "^0.1.1",
15
+ "solidstep": "^0.3.5",
16
16
  "vinxi": "^0.5.8"
17
17
  },
18
18
  "devDependencies": {
@@ -0,0 +1,9 @@
1
+ // Stub for `node:sqlite`.
2
+ //
3
+ // Nitro's default Node database connector bundles an (unused) `import 'node:sqlite'`
4
+ // into the server output. That builtin only exists in Node 22.5+, so on Node 20/21
5
+ // the server crashes at startup even though no database is ever used. This app has
6
+ // no database, so app.config.ts aliases `node:sqlite` to this empty stub.
7
+ export class DatabaseSync {}
8
+ export class StatementSync {}
9
+ export default { DatabaseSync, StatementSync };
@@ -8,9 +8,10 @@
8
8
  "jsx": "preserve",
9
9
  "jsxImportSource": "solid-js",
10
10
  "allowJs": true,
11
- "checkJs": true,
11
+ "checkJs": false,
12
12
  "noEmit": true,
13
13
  "types": ["vinxi/client"],
14
- "isolatedModules": true
14
+ "isolatedModules": true,
15
+ "skipLibCheck": true
15
16
  }
16
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@varlabs/create-solidstep",
3
- "version": "0.1.7",
3
+ "version": "0.2.0",
4
4
  "description": "Next Step SolidJS CLI for building web applications.",
5
5
  "type": "module",
6
6
  "author": "HamzaKV <hamzakv333@gmail.com>",