create-lacspace-app 2.11.0 → 2.12.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/README.md +4 -0
- package/dist/index.js +95 -0
- package/dist/lib.cjs +95 -0
- package/dist/lib.js +95 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,6 +20,10 @@ npx create-lacspace-app my-app --template saas --fullstack
|
|
|
20
20
|
|
|
21
21
|
You choose the *kind* of site you're building. It writes a **real Next.js 15 + React 19 + Tailwind v4 app** — not a hello-world, but a genuinely **polished, modern site**: a fluid `clamp()` type scale, tight display headings, a refined light **and** dark palette, glass chrome, soft layered shadows, a smooth logo marquee, animated counters, scroll reveals and a shimmering primary CTA — every page filled in, an SEO stack wired end-to-end, and a **26-component UI kit** you can drop in anywhere.
|
|
22
22
|
|
|
23
|
+
> **New in v2.12 — real-time & privacy (16 add-ons).** Two more keyless, no-vendor add-ons:
|
|
24
|
+
> - **`realtime`** — live server→browser updates over **Server-Sent Events** (`@lacspace/sse`): a channel hub + a `/live` stream + a React `useSSE` feed. No WebSocket server. Full-stack.
|
|
25
|
+
> - **`consent`** — **GDPR-friendly cookie consent** (`@lacspace/consent`): a drop-in `<ConsentBanner/>`, per-category choices, cookie persistence and `whenConsent()` script-gating. Frontend.
|
|
26
|
+
>
|
|
23
27
|
> **New in v2.11 — the Web Engagement Kit (14 add-ons).** Three keyless, no-vendor add-ons on brand-new `@lacspace` packages:
|
|
24
28
|
> - **`push`** — real **browser push notifications** (`@lacspace/web-push`): keyless VAPID, no Firebase/FCM. Generates a service worker, a subscribe button, and a backend that stores subscriptions and sends. Full-stack.
|
|
25
29
|
> - **`captcha`** — a **privacy-friendly proof-of-work CAPTCHA** (`@lacspace/captcha`): no Google/Cloudflare, no keys, no tracking. A drop-in widget + a backend verify route. Full-stack.
|
package/dist/index.js
CHANGED
|
@@ -2402,6 +2402,38 @@ var FEATURES = [
|
|
|
2402
2402
|
"Push works on https (or localhost); the generated public/sw.js receives the notifications."
|
|
2403
2403
|
],
|
|
2404
2404
|
learn: "https://developer.lacspace.com/packages/web-push"
|
|
2405
|
+
},
|
|
2406
|
+
{
|
|
2407
|
+
key: "consent",
|
|
2408
|
+
label: "Cookie consent",
|
|
2409
|
+
description: "GDPR-friendly cookie consent (@lacspace/consent) \u2014 a drop-in <ConsentBanner/>, per-category choices, cookie persistence and whenConsent() script-gating. Frontend, any template.",
|
|
2410
|
+
deps: { "@lacspace/consent": "^1.0.0" },
|
|
2411
|
+
files: () => ({ "components/consent.tsx": consentComponent() }),
|
|
2412
|
+
nextSteps: [
|
|
2413
|
+
"Render <ConsentBanner/> once in app/layout.tsx: import { ConsentBanner } from '@/components/consent'.",
|
|
2414
|
+
"Gate analytics/marketing scripts: whenConsent(consent, 'analytics', () => { /* load tag */ }).",
|
|
2415
|
+
"The choice is stored in a cookie \u2014 gate server-side too with parseConsentCookie()."
|
|
2416
|
+
],
|
|
2417
|
+
learn: "https://developer.lacspace.com/packages/consent"
|
|
2418
|
+
},
|
|
2419
|
+
{
|
|
2420
|
+
key: "realtime",
|
|
2421
|
+
label: "Real-time (SSE)",
|
|
2422
|
+
description: "Live server-to-browser updates over Server-Sent Events (@lacspace/sse) \u2014 a channel hub + a /live stream + a React useSSE feed. No WebSocket server. Full-stack.",
|
|
2423
|
+
requiresBackend: true,
|
|
2424
|
+
deps: { "@lacspace/sse": "^1.0.0" },
|
|
2425
|
+
files: () => ({ "app/live/page.tsx": realtimeLivePage() }),
|
|
2426
|
+
backend: () => ({
|
|
2427
|
+
deps: { "@lacspace/sse": "^1.0.0" },
|
|
2428
|
+
files: { "src/routes/live.ts": realtimeRoutesBackend() },
|
|
2429
|
+
routes: [{ path: "/live", handler: "liveRoutes", auth: false, importLine: 'import liveRoutes from "./live.js";' }]
|
|
2430
|
+
}),
|
|
2431
|
+
nextSteps: [
|
|
2432
|
+
"Open http://localhost:3000/live in two tabs; click 'Broadcast' in one \u2014 the other updates instantly.",
|
|
2433
|
+
"Push from anywhere on the server: hub.broadcast({ event: 'message', data }) in src/routes/live.ts.",
|
|
2434
|
+
"SSE auto-reconnects and needs no WebSocket server \u2014 great for notifications and live feeds."
|
|
2435
|
+
],
|
|
2436
|
+
learn: "https://developer.lacspace.com/packages/sse"
|
|
2405
2437
|
}
|
|
2406
2438
|
];
|
|
2407
2439
|
var aiChatRoute = (_ctx) => `import { resolveConfig } from "@lacspace/providers";
|
|
@@ -6920,6 +6952,69 @@ router.post("/send", asyncHandler(async (req, res) => {
|
|
|
6920
6952
|
res.json({ sent: results.filter((r) => r.result && !r.result.expired).length, removed: expired.length });
|
|
6921
6953
|
}));
|
|
6922
6954
|
|
|
6955
|
+
export default router;
|
|
6956
|
+
`;
|
|
6957
|
+
var consentComponent = () => `"use client";
|
|
6958
|
+
// how this works: re-export the consent UI + API from one component. Render
|
|
6959
|
+
// <ConsentBanner/> once in app/layout.tsx; gate scripts with whenConsent().
|
|
6960
|
+
export { ConsentBanner, useConsent } from "@lacspace/consent/react";
|
|
6961
|
+
export { consent, whenConsent } from "@lacspace/consent";
|
|
6962
|
+
`;
|
|
6963
|
+
var realtimeLivePage = () => `"use client";
|
|
6964
|
+
import { useState } from "react";
|
|
6965
|
+
import { useSSE } from "@lacspace/sse/react";
|
|
6966
|
+
|
|
6967
|
+
const API = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:4000";
|
|
6968
|
+
|
|
6969
|
+
export default function LivePage() {
|
|
6970
|
+
const [log, setLog] = useState<string[]>([]);
|
|
6971
|
+
const { status } = useSSE(API + "/live", {
|
|
6972
|
+
onEvent: { message: (m) => setLog((prev) => [String((m as { text?: string }).text ?? ""), ...prev].slice(0, 50)) },
|
|
6973
|
+
});
|
|
6974
|
+
|
|
6975
|
+
async function broadcast() {
|
|
6976
|
+
await fetch(API + "/live/broadcast", {
|
|
6977
|
+
method: "POST",
|
|
6978
|
+
headers: { "Content-Type": "application/json" },
|
|
6979
|
+
body: JSON.stringify({ text: "Hello at " + new Date().toLocaleTimeString() }),
|
|
6980
|
+
});
|
|
6981
|
+
}
|
|
6982
|
+
|
|
6983
|
+
return (
|
|
6984
|
+
<main className="mx-auto max-w-lg px-6 py-16">
|
|
6985
|
+
<h1 className="text-2xl font-bold">Live feed</h1>
|
|
6986
|
+
<p className="mt-1 text-muted">Server-Sent Events \u2014 status: {status}.</p>
|
|
6987
|
+
<button className="mt-4 rounded-xl border border-hairline px-4 py-2" onClick={broadcast}>Broadcast a message</button>
|
|
6988
|
+
<ul className="mt-6 space-y-2">
|
|
6989
|
+
{log.map((line, i) => (<li key={i} className="rounded-xl border border-hairline p-3">{line}</li>))}
|
|
6990
|
+
</ul>
|
|
6991
|
+
</main>
|
|
6992
|
+
);
|
|
6993
|
+
}
|
|
6994
|
+
`;
|
|
6995
|
+
var realtimeRoutesBackend = () => `import express from "express";
|
|
6996
|
+
import { SSEHub, sseHandler } from "@lacspace/sse";
|
|
6997
|
+
import { asyncHandler } from "../http.js";
|
|
6998
|
+
|
|
6999
|
+
// how this works: one shared hub holds every open SSE connection. GET /live opens
|
|
7000
|
+
// a stream; POST /live/broadcast pushes a "message" event to everyone connected.
|
|
7001
|
+
// Push from anywhere in your app by importing this hub and calling hub.broadcast().
|
|
7002
|
+
const router = express.Router();
|
|
7003
|
+
const hub = new SSEHub();
|
|
7004
|
+
hub.startHeartbeat();
|
|
7005
|
+
|
|
7006
|
+
router.get("/", (_req, res) => {
|
|
7007
|
+
const client = sseHandler(res, { onClose: () => hub.remove(client) });
|
|
7008
|
+
hub.add(client);
|
|
7009
|
+
client.send({ event: "message", data: { text: "Connected to the live feed." } });
|
|
7010
|
+
});
|
|
7011
|
+
|
|
7012
|
+
router.post("/broadcast", asyncHandler(async (req, res) => {
|
|
7013
|
+
const text = typeof req.body?.text === "string" ? req.body.text : "ping";
|
|
7014
|
+
const sent = hub.broadcast({ event: "message", data: { text } });
|
|
7015
|
+
res.json({ sent });
|
|
7016
|
+
}));
|
|
7017
|
+
|
|
6923
7018
|
export default router;
|
|
6924
7019
|
`;
|
|
6925
7020
|
var scope = (ctx) => `@${ctx.name}`;
|
package/dist/lib.cjs
CHANGED
|
@@ -2406,6 +2406,38 @@ var FEATURES = [
|
|
|
2406
2406
|
"Push works on https (or localhost); the generated public/sw.js receives the notifications."
|
|
2407
2407
|
],
|
|
2408
2408
|
learn: "https://developer.lacspace.com/packages/web-push"
|
|
2409
|
+
},
|
|
2410
|
+
{
|
|
2411
|
+
key: "consent",
|
|
2412
|
+
label: "Cookie consent",
|
|
2413
|
+
description: "GDPR-friendly cookie consent (@lacspace/consent) \u2014 a drop-in <ConsentBanner/>, per-category choices, cookie persistence and whenConsent() script-gating. Frontend, any template.",
|
|
2414
|
+
deps: { "@lacspace/consent": "^1.0.0" },
|
|
2415
|
+
files: () => ({ "components/consent.tsx": consentComponent() }),
|
|
2416
|
+
nextSteps: [
|
|
2417
|
+
"Render <ConsentBanner/> once in app/layout.tsx: import { ConsentBanner } from '@/components/consent'.",
|
|
2418
|
+
"Gate analytics/marketing scripts: whenConsent(consent, 'analytics', () => { /* load tag */ }).",
|
|
2419
|
+
"The choice is stored in a cookie \u2014 gate server-side too with parseConsentCookie()."
|
|
2420
|
+
],
|
|
2421
|
+
learn: "https://developer.lacspace.com/packages/consent"
|
|
2422
|
+
},
|
|
2423
|
+
{
|
|
2424
|
+
key: "realtime",
|
|
2425
|
+
label: "Real-time (SSE)",
|
|
2426
|
+
description: "Live server-to-browser updates over Server-Sent Events (@lacspace/sse) \u2014 a channel hub + a /live stream + a React useSSE feed. No WebSocket server. Full-stack.",
|
|
2427
|
+
requiresBackend: true,
|
|
2428
|
+
deps: { "@lacspace/sse": "^1.0.0" },
|
|
2429
|
+
files: () => ({ "app/live/page.tsx": realtimeLivePage() }),
|
|
2430
|
+
backend: () => ({
|
|
2431
|
+
deps: { "@lacspace/sse": "^1.0.0" },
|
|
2432
|
+
files: { "src/routes/live.ts": realtimeRoutesBackend() },
|
|
2433
|
+
routes: [{ path: "/live", handler: "liveRoutes", auth: false, importLine: 'import liveRoutes from "./live.js";' }]
|
|
2434
|
+
}),
|
|
2435
|
+
nextSteps: [
|
|
2436
|
+
"Open http://localhost:3000/live in two tabs; click 'Broadcast' in one \u2014 the other updates instantly.",
|
|
2437
|
+
"Push from anywhere on the server: hub.broadcast({ event: 'message', data }) in src/routes/live.ts.",
|
|
2438
|
+
"SSE auto-reconnects and needs no WebSocket server \u2014 great for notifications and live feeds."
|
|
2439
|
+
],
|
|
2440
|
+
learn: "https://developer.lacspace.com/packages/sse"
|
|
2409
2441
|
}
|
|
2410
2442
|
];
|
|
2411
2443
|
var aiChatRoute = (_ctx) => `import { resolveConfig } from "@lacspace/providers";
|
|
@@ -6924,6 +6956,69 @@ router.post("/send", asyncHandler(async (req, res) => {
|
|
|
6924
6956
|
res.json({ sent: results.filter((r) => r.result && !r.result.expired).length, removed: expired.length });
|
|
6925
6957
|
}));
|
|
6926
6958
|
|
|
6959
|
+
export default router;
|
|
6960
|
+
`;
|
|
6961
|
+
var consentComponent = () => `"use client";
|
|
6962
|
+
// how this works: re-export the consent UI + API from one component. Render
|
|
6963
|
+
// <ConsentBanner/> once in app/layout.tsx; gate scripts with whenConsent().
|
|
6964
|
+
export { ConsentBanner, useConsent } from "@lacspace/consent/react";
|
|
6965
|
+
export { consent, whenConsent } from "@lacspace/consent";
|
|
6966
|
+
`;
|
|
6967
|
+
var realtimeLivePage = () => `"use client";
|
|
6968
|
+
import { useState } from "react";
|
|
6969
|
+
import { useSSE } from "@lacspace/sse/react";
|
|
6970
|
+
|
|
6971
|
+
const API = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:4000";
|
|
6972
|
+
|
|
6973
|
+
export default function LivePage() {
|
|
6974
|
+
const [log, setLog] = useState<string[]>([]);
|
|
6975
|
+
const { status } = useSSE(API + "/live", {
|
|
6976
|
+
onEvent: { message: (m) => setLog((prev) => [String((m as { text?: string }).text ?? ""), ...prev].slice(0, 50)) },
|
|
6977
|
+
});
|
|
6978
|
+
|
|
6979
|
+
async function broadcast() {
|
|
6980
|
+
await fetch(API + "/live/broadcast", {
|
|
6981
|
+
method: "POST",
|
|
6982
|
+
headers: { "Content-Type": "application/json" },
|
|
6983
|
+
body: JSON.stringify({ text: "Hello at " + new Date().toLocaleTimeString() }),
|
|
6984
|
+
});
|
|
6985
|
+
}
|
|
6986
|
+
|
|
6987
|
+
return (
|
|
6988
|
+
<main className="mx-auto max-w-lg px-6 py-16">
|
|
6989
|
+
<h1 className="text-2xl font-bold">Live feed</h1>
|
|
6990
|
+
<p className="mt-1 text-muted">Server-Sent Events \u2014 status: {status}.</p>
|
|
6991
|
+
<button className="mt-4 rounded-xl border border-hairline px-4 py-2" onClick={broadcast}>Broadcast a message</button>
|
|
6992
|
+
<ul className="mt-6 space-y-2">
|
|
6993
|
+
{log.map((line, i) => (<li key={i} className="rounded-xl border border-hairline p-3">{line}</li>))}
|
|
6994
|
+
</ul>
|
|
6995
|
+
</main>
|
|
6996
|
+
);
|
|
6997
|
+
}
|
|
6998
|
+
`;
|
|
6999
|
+
var realtimeRoutesBackend = () => `import express from "express";
|
|
7000
|
+
import { SSEHub, sseHandler } from "@lacspace/sse";
|
|
7001
|
+
import { asyncHandler } from "../http.js";
|
|
7002
|
+
|
|
7003
|
+
// how this works: one shared hub holds every open SSE connection. GET /live opens
|
|
7004
|
+
// a stream; POST /live/broadcast pushes a "message" event to everyone connected.
|
|
7005
|
+
// Push from anywhere in your app by importing this hub and calling hub.broadcast().
|
|
7006
|
+
const router = express.Router();
|
|
7007
|
+
const hub = new SSEHub();
|
|
7008
|
+
hub.startHeartbeat();
|
|
7009
|
+
|
|
7010
|
+
router.get("/", (_req, res) => {
|
|
7011
|
+
const client = sseHandler(res, { onClose: () => hub.remove(client) });
|
|
7012
|
+
hub.add(client);
|
|
7013
|
+
client.send({ event: "message", data: { text: "Connected to the live feed." } });
|
|
7014
|
+
});
|
|
7015
|
+
|
|
7016
|
+
router.post("/broadcast", asyncHandler(async (req, res) => {
|
|
7017
|
+
const text = typeof req.body?.text === "string" ? req.body.text : "ping";
|
|
7018
|
+
const sent = hub.broadcast({ event: "message", data: { text } });
|
|
7019
|
+
res.json({ sent });
|
|
7020
|
+
}));
|
|
7021
|
+
|
|
6927
7022
|
export default router;
|
|
6928
7023
|
`;
|
|
6929
7024
|
var scope = (ctx) => `@${ctx.name}`;
|
package/dist/lib.js
CHANGED
|
@@ -2403,6 +2403,38 @@ var FEATURES = [
|
|
|
2403
2403
|
"Push works on https (or localhost); the generated public/sw.js receives the notifications."
|
|
2404
2404
|
],
|
|
2405
2405
|
learn: "https://developer.lacspace.com/packages/web-push"
|
|
2406
|
+
},
|
|
2407
|
+
{
|
|
2408
|
+
key: "consent",
|
|
2409
|
+
label: "Cookie consent",
|
|
2410
|
+
description: "GDPR-friendly cookie consent (@lacspace/consent) \u2014 a drop-in <ConsentBanner/>, per-category choices, cookie persistence and whenConsent() script-gating. Frontend, any template.",
|
|
2411
|
+
deps: { "@lacspace/consent": "^1.0.0" },
|
|
2412
|
+
files: () => ({ "components/consent.tsx": consentComponent() }),
|
|
2413
|
+
nextSteps: [
|
|
2414
|
+
"Render <ConsentBanner/> once in app/layout.tsx: import { ConsentBanner } from '@/components/consent'.",
|
|
2415
|
+
"Gate analytics/marketing scripts: whenConsent(consent, 'analytics', () => { /* load tag */ }).",
|
|
2416
|
+
"The choice is stored in a cookie \u2014 gate server-side too with parseConsentCookie()."
|
|
2417
|
+
],
|
|
2418
|
+
learn: "https://developer.lacspace.com/packages/consent"
|
|
2419
|
+
},
|
|
2420
|
+
{
|
|
2421
|
+
key: "realtime",
|
|
2422
|
+
label: "Real-time (SSE)",
|
|
2423
|
+
description: "Live server-to-browser updates over Server-Sent Events (@lacspace/sse) \u2014 a channel hub + a /live stream + a React useSSE feed. No WebSocket server. Full-stack.",
|
|
2424
|
+
requiresBackend: true,
|
|
2425
|
+
deps: { "@lacspace/sse": "^1.0.0" },
|
|
2426
|
+
files: () => ({ "app/live/page.tsx": realtimeLivePage() }),
|
|
2427
|
+
backend: () => ({
|
|
2428
|
+
deps: { "@lacspace/sse": "^1.0.0" },
|
|
2429
|
+
files: { "src/routes/live.ts": realtimeRoutesBackend() },
|
|
2430
|
+
routes: [{ path: "/live", handler: "liveRoutes", auth: false, importLine: 'import liveRoutes from "./live.js";' }]
|
|
2431
|
+
}),
|
|
2432
|
+
nextSteps: [
|
|
2433
|
+
"Open http://localhost:3000/live in two tabs; click 'Broadcast' in one \u2014 the other updates instantly.",
|
|
2434
|
+
"Push from anywhere on the server: hub.broadcast({ event: 'message', data }) in src/routes/live.ts.",
|
|
2435
|
+
"SSE auto-reconnects and needs no WebSocket server \u2014 great for notifications and live feeds."
|
|
2436
|
+
],
|
|
2437
|
+
learn: "https://developer.lacspace.com/packages/sse"
|
|
2406
2438
|
}
|
|
2407
2439
|
];
|
|
2408
2440
|
var aiChatRoute = (_ctx) => `import { resolveConfig } from "@lacspace/providers";
|
|
@@ -6921,6 +6953,69 @@ router.post("/send", asyncHandler(async (req, res) => {
|
|
|
6921
6953
|
res.json({ sent: results.filter((r) => r.result && !r.result.expired).length, removed: expired.length });
|
|
6922
6954
|
}));
|
|
6923
6955
|
|
|
6956
|
+
export default router;
|
|
6957
|
+
`;
|
|
6958
|
+
var consentComponent = () => `"use client";
|
|
6959
|
+
// how this works: re-export the consent UI + API from one component. Render
|
|
6960
|
+
// <ConsentBanner/> once in app/layout.tsx; gate scripts with whenConsent().
|
|
6961
|
+
export { ConsentBanner, useConsent } from "@lacspace/consent/react";
|
|
6962
|
+
export { consent, whenConsent } from "@lacspace/consent";
|
|
6963
|
+
`;
|
|
6964
|
+
var realtimeLivePage = () => `"use client";
|
|
6965
|
+
import { useState } from "react";
|
|
6966
|
+
import { useSSE } from "@lacspace/sse/react";
|
|
6967
|
+
|
|
6968
|
+
const API = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:4000";
|
|
6969
|
+
|
|
6970
|
+
export default function LivePage() {
|
|
6971
|
+
const [log, setLog] = useState<string[]>([]);
|
|
6972
|
+
const { status } = useSSE(API + "/live", {
|
|
6973
|
+
onEvent: { message: (m) => setLog((prev) => [String((m as { text?: string }).text ?? ""), ...prev].slice(0, 50)) },
|
|
6974
|
+
});
|
|
6975
|
+
|
|
6976
|
+
async function broadcast() {
|
|
6977
|
+
await fetch(API + "/live/broadcast", {
|
|
6978
|
+
method: "POST",
|
|
6979
|
+
headers: { "Content-Type": "application/json" },
|
|
6980
|
+
body: JSON.stringify({ text: "Hello at " + new Date().toLocaleTimeString() }),
|
|
6981
|
+
});
|
|
6982
|
+
}
|
|
6983
|
+
|
|
6984
|
+
return (
|
|
6985
|
+
<main className="mx-auto max-w-lg px-6 py-16">
|
|
6986
|
+
<h1 className="text-2xl font-bold">Live feed</h1>
|
|
6987
|
+
<p className="mt-1 text-muted">Server-Sent Events \u2014 status: {status}.</p>
|
|
6988
|
+
<button className="mt-4 rounded-xl border border-hairline px-4 py-2" onClick={broadcast}>Broadcast a message</button>
|
|
6989
|
+
<ul className="mt-6 space-y-2">
|
|
6990
|
+
{log.map((line, i) => (<li key={i} className="rounded-xl border border-hairline p-3">{line}</li>))}
|
|
6991
|
+
</ul>
|
|
6992
|
+
</main>
|
|
6993
|
+
);
|
|
6994
|
+
}
|
|
6995
|
+
`;
|
|
6996
|
+
var realtimeRoutesBackend = () => `import express from "express";
|
|
6997
|
+
import { SSEHub, sseHandler } from "@lacspace/sse";
|
|
6998
|
+
import { asyncHandler } from "../http.js";
|
|
6999
|
+
|
|
7000
|
+
// how this works: one shared hub holds every open SSE connection. GET /live opens
|
|
7001
|
+
// a stream; POST /live/broadcast pushes a "message" event to everyone connected.
|
|
7002
|
+
// Push from anywhere in your app by importing this hub and calling hub.broadcast().
|
|
7003
|
+
const router = express.Router();
|
|
7004
|
+
const hub = new SSEHub();
|
|
7005
|
+
hub.startHeartbeat();
|
|
7006
|
+
|
|
7007
|
+
router.get("/", (_req, res) => {
|
|
7008
|
+
const client = sseHandler(res, { onClose: () => hub.remove(client) });
|
|
7009
|
+
hub.add(client);
|
|
7010
|
+
client.send({ event: "message", data: { text: "Connected to the live feed." } });
|
|
7011
|
+
});
|
|
7012
|
+
|
|
7013
|
+
router.post("/broadcast", asyncHandler(async (req, res) => {
|
|
7014
|
+
const text = typeof req.body?.text === "string" ? req.body.text : "ping";
|
|
7015
|
+
const sent = hub.broadcast({ event: "message", data: { text } });
|
|
7016
|
+
res.json({ sent });
|
|
7017
|
+
}));
|
|
7018
|
+
|
|
6924
7019
|
export default router;
|
|
6925
7020
|
`;
|
|
6926
7021
|
var scope = (ctx) => `@${ctx.name}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-lacspace-app",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.0",
|
|
4
4
|
"description": "Scaffold a beautiful, production-ready Next.js app from a Lacspace template — portfolio, business, e-commerce, SaaS, blog, docs, dashboard, restaurant or marketplace — pre-wired with SEO, security headers, sitemap and robots. Use it as a CLI (like create-next-app, but you start gorgeous) or import it as a library to generate projects programmatically.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|