@cloudwerk/vite-plugin 0.5.0 → 0.6.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/dist/index.d.ts +11 -2
- package/dist/index.js +267 -3
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
|
-
import { CloudwerkConfig, RouteManifest, ScanResult } from '@cloudwerk/core/build';
|
|
2
|
+
import { CloudwerkConfig, RouteManifest, ScanResult, QueueManifest, ServiceManifest } from '@cloudwerk/core/build';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @cloudwerk/vite-plugin - Types
|
|
@@ -150,6 +150,15 @@ declare function cloudwerkPlugin(options?: CloudwerkVitePluginOptions): Plugin;
|
|
|
150
150
|
* a Hono app with all routes registered from the file-based routing manifest.
|
|
151
151
|
*/
|
|
152
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Options for generating server entry.
|
|
155
|
+
*/
|
|
156
|
+
interface GenerateServerEntryOptions {
|
|
157
|
+
/** Queue manifest if queues are configured */
|
|
158
|
+
queueManifest?: QueueManifest | null;
|
|
159
|
+
/** Service manifest if services are configured */
|
|
160
|
+
serviceManifest?: ServiceManifest | null;
|
|
161
|
+
}
|
|
153
162
|
/**
|
|
154
163
|
* Generate the server entry module code.
|
|
155
164
|
*
|
|
@@ -165,7 +174,7 @@ declare function cloudwerkPlugin(options?: CloudwerkVitePluginOptions): Plugin;
|
|
|
165
174
|
* @param options - Resolved plugin options
|
|
166
175
|
* @returns Generated TypeScript/JavaScript code
|
|
167
176
|
*/
|
|
168
|
-
declare function generateServerEntry(manifest: RouteManifest, scanResult: ScanResult, options: ResolvedCloudwerkOptions): string;
|
|
177
|
+
declare function generateServerEntry(manifest: RouteManifest, scanResult: ScanResult, options: ResolvedCloudwerkOptions, entryOptions?: GenerateServerEntryOptions): string;
|
|
169
178
|
|
|
170
179
|
/**
|
|
171
180
|
* Client Entry Virtual Module Generator
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,14 @@ import {
|
|
|
12
12
|
resolveRoutesPath,
|
|
13
13
|
hasUseClientDirective,
|
|
14
14
|
generateComponentId,
|
|
15
|
-
ROUTE_FILE_NAMES
|
|
15
|
+
ROUTE_FILE_NAMES,
|
|
16
|
+
scanQueues,
|
|
17
|
+
buildQueueManifest,
|
|
18
|
+
QUEUES_DIR,
|
|
19
|
+
scanServices,
|
|
20
|
+
buildServiceManifest,
|
|
21
|
+
SERVICES_DIR,
|
|
22
|
+
SERVICE_FILE_NAME
|
|
16
23
|
} from "@cloudwerk/core/build";
|
|
17
24
|
|
|
18
25
|
// src/types.ts
|
|
@@ -29,7 +36,9 @@ var RESOLVED_VIRTUAL_IDS = {
|
|
|
29
36
|
|
|
30
37
|
// src/virtual-modules/server-entry.ts
|
|
31
38
|
import * as path from "path";
|
|
32
|
-
function generateServerEntry(manifest, scanResult, options) {
|
|
39
|
+
function generateServerEntry(manifest, scanResult, options, entryOptions) {
|
|
40
|
+
const queueManifest = entryOptions?.queueManifest;
|
|
41
|
+
const serviceManifest = entryOptions?.serviceManifest;
|
|
33
42
|
const imports = [];
|
|
34
43
|
const pageRegistrations = [];
|
|
35
44
|
const routeRegistrations = [];
|
|
@@ -532,8 +541,137 @@ app.onError(async (err, c) => {
|
|
|
532
541
|
// ============================================================================
|
|
533
542
|
|
|
534
543
|
export default app
|
|
544
|
+
${generateQueueExports(queueManifest)}
|
|
545
|
+
${generateServiceRegistration(serviceManifest)}
|
|
535
546
|
`;
|
|
536
547
|
}
|
|
548
|
+
function generateQueueExports(queueManifest) {
|
|
549
|
+
if (!queueManifest || queueManifest.queues.length === 0) {
|
|
550
|
+
return "";
|
|
551
|
+
}
|
|
552
|
+
const lines = [];
|
|
553
|
+
const imports = [];
|
|
554
|
+
const queueHandlers = [];
|
|
555
|
+
lines.push("");
|
|
556
|
+
lines.push("// ============================================================================");
|
|
557
|
+
lines.push("// Queue Consumer Handlers");
|
|
558
|
+
lines.push("// ============================================================================");
|
|
559
|
+
lines.push("");
|
|
560
|
+
for (let i = 0; i < queueManifest.queues.length; i++) {
|
|
561
|
+
const queue = queueManifest.queues[i];
|
|
562
|
+
const varName = `queueDef_${i}`;
|
|
563
|
+
imports.push(`import ${varName} from '${queue.absolutePath}'`);
|
|
564
|
+
queueHandlers.push(`
|
|
565
|
+
/**
|
|
566
|
+
* Queue consumer handler for '${queue.name}'
|
|
567
|
+
*/
|
|
568
|
+
async function handle_${queue.name}_queue(batch, env, ctx) {
|
|
569
|
+
const definition = ${varName}
|
|
570
|
+
|
|
571
|
+
// Create message wrappers
|
|
572
|
+
const messages = batch.messages.map((msg) => ({
|
|
573
|
+
id: msg.id,
|
|
574
|
+
body: msg.body,
|
|
575
|
+
timestamp: new Date(msg.timestamp),
|
|
576
|
+
attempts: msg.attempts,
|
|
577
|
+
ack: () => msg.ack(),
|
|
578
|
+
retry: (options) => msg.retry(options),
|
|
579
|
+
deadLetter: (reason) => {
|
|
580
|
+
// Mark for DLQ if configured
|
|
581
|
+
if (definition.config?.deadLetterQueue) {
|
|
582
|
+
msg.retry({ delaySeconds: 0 })
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
}))
|
|
586
|
+
|
|
587
|
+
// Validate messages if schema is defined
|
|
588
|
+
if (definition.schema) {
|
|
589
|
+
for (const message of messages) {
|
|
590
|
+
const result = definition.schema.safeParse(message.body)
|
|
591
|
+
if (!result.success) {
|
|
592
|
+
console.error('Queue message validation failed:', result.error)
|
|
593
|
+
message.retry({ delaySeconds: 60 })
|
|
594
|
+
return
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
try {
|
|
600
|
+
// Use batch processor if available
|
|
601
|
+
if (definition.processBatch) {
|
|
602
|
+
await definition.processBatch(messages)
|
|
603
|
+
} else if (definition.process) {
|
|
604
|
+
// Process messages individually
|
|
605
|
+
for (const message of messages) {
|
|
606
|
+
try {
|
|
607
|
+
await definition.process(message)
|
|
608
|
+
} catch (error) {
|
|
609
|
+
if (definition.onError) {
|
|
610
|
+
await definition.onError(error, message)
|
|
611
|
+
} else {
|
|
612
|
+
throw error
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
} catch (error) {
|
|
618
|
+
console.error('Queue processing error:', error)
|
|
619
|
+
// Retry all messages
|
|
620
|
+
batch.retryAll()
|
|
621
|
+
}
|
|
622
|
+
}`);
|
|
623
|
+
}
|
|
624
|
+
lines.push(imports.join("\n"));
|
|
625
|
+
lines.push(queueHandlers.join("\n"));
|
|
626
|
+
lines.push("");
|
|
627
|
+
lines.push("/**");
|
|
628
|
+
lines.push(" * Main queue handler that routes to specific queue handlers.");
|
|
629
|
+
lines.push(" * Export this as the `queue` handler in your worker.");
|
|
630
|
+
lines.push(" */");
|
|
631
|
+
lines.push("export async function queue(batch, env, ctx) {");
|
|
632
|
+
lines.push(" const queueName = batch.queue");
|
|
633
|
+
lines.push("");
|
|
634
|
+
for (const queue of queueManifest.queues) {
|
|
635
|
+
lines.push(` if (queueName === '${queue.queueName}') {`);
|
|
636
|
+
lines.push(` return handle_${queue.name}_queue(batch, env, ctx)`);
|
|
637
|
+
lines.push(" }");
|
|
638
|
+
lines.push("");
|
|
639
|
+
}
|
|
640
|
+
lines.push(" console.warn(\\`Unknown queue: \\${queueName}\\`)");
|
|
641
|
+
lines.push("}");
|
|
642
|
+
return lines.join("\n");
|
|
643
|
+
}
|
|
644
|
+
function generateServiceRegistration(serviceManifest) {
|
|
645
|
+
if (!serviceManifest || serviceManifest.services.length === 0) {
|
|
646
|
+
return "";
|
|
647
|
+
}
|
|
648
|
+
const lines = [];
|
|
649
|
+
const imports = [];
|
|
650
|
+
const registrations = [];
|
|
651
|
+
lines.push("");
|
|
652
|
+
lines.push("// ============================================================================");
|
|
653
|
+
lines.push("// Service Registration");
|
|
654
|
+
lines.push("// ============================================================================");
|
|
655
|
+
lines.push("");
|
|
656
|
+
imports.push("import { registerLocalService } from '@cloudwerk/core/bindings'");
|
|
657
|
+
for (let i = 0; i < serviceManifest.services.length; i++) {
|
|
658
|
+
const service = serviceManifest.services[i];
|
|
659
|
+
const varName = `serviceDef_${i}`;
|
|
660
|
+
imports.push(`import ${varName} from '${service.absolutePath}'`);
|
|
661
|
+
if (service.mode === "local") {
|
|
662
|
+
registrations.push(`registerLocalService('${service.name}', ${varName})`);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
lines.push(imports.join("\n"));
|
|
666
|
+
lines.push("");
|
|
667
|
+
if (registrations.length > 0) {
|
|
668
|
+
lines.push("// Register local services");
|
|
669
|
+
for (const reg of registrations) {
|
|
670
|
+
lines.push(reg);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return lines.join("\n");
|
|
674
|
+
}
|
|
537
675
|
|
|
538
676
|
// src/virtual-modules/client-entry.ts
|
|
539
677
|
function generateClientEntry(clientComponents, options) {
|
|
@@ -1437,6 +1575,55 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1437
1575
|
console.log(`[cloudwerk] Found ${state.manifest.routes.length} routes`);
|
|
1438
1576
|
}
|
|
1439
1577
|
}
|
|
1578
|
+
async function buildQueueManifestIfExists(root) {
|
|
1579
|
+
if (!state) {
|
|
1580
|
+
throw new Error("Plugin state not initialized");
|
|
1581
|
+
}
|
|
1582
|
+
const queuesPath = path3.resolve(root, state.options.appDir, QUEUES_DIR);
|
|
1583
|
+
try {
|
|
1584
|
+
await fs2.promises.access(queuesPath);
|
|
1585
|
+
} catch {
|
|
1586
|
+
state.queueScanResult = null;
|
|
1587
|
+
state.queueManifest = null;
|
|
1588
|
+
return;
|
|
1589
|
+
}
|
|
1590
|
+
state.queueScanResult = await scanQueues(
|
|
1591
|
+
path3.resolve(root, state.options.appDir),
|
|
1592
|
+
{ extensions: state.options.config.extensions }
|
|
1593
|
+
);
|
|
1594
|
+
state.queueManifest = buildQueueManifest(
|
|
1595
|
+
state.queueScanResult,
|
|
1596
|
+
root,
|
|
1597
|
+
{ appName: "cloudwerk" }
|
|
1598
|
+
);
|
|
1599
|
+
if (state.options.verbose && state.queueManifest.queues.length > 0) {
|
|
1600
|
+
console.log(`[cloudwerk] Found ${state.queueManifest.queues.length} queue(s)`);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
async function buildServiceManifestIfExists(root) {
|
|
1604
|
+
if (!state) {
|
|
1605
|
+
throw new Error("Plugin state not initialized");
|
|
1606
|
+
}
|
|
1607
|
+
const servicesPath = path3.resolve(root, state.options.appDir, SERVICES_DIR);
|
|
1608
|
+
try {
|
|
1609
|
+
await fs2.promises.access(servicesPath);
|
|
1610
|
+
} catch {
|
|
1611
|
+
state.serviceScanResult = null;
|
|
1612
|
+
state.serviceManifest = null;
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
state.serviceScanResult = await scanServices(
|
|
1616
|
+
path3.resolve(root, state.options.appDir),
|
|
1617
|
+
{ extensions: state.options.config.extensions }
|
|
1618
|
+
);
|
|
1619
|
+
state.serviceManifest = buildServiceManifest(
|
|
1620
|
+
state.serviceScanResult,
|
|
1621
|
+
root
|
|
1622
|
+
);
|
|
1623
|
+
if (state.options.verbose && state.serviceManifest.services.length > 0) {
|
|
1624
|
+
console.log(`[cloudwerk] Found ${state.serviceManifest.services.length} service(s)`);
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1440
1627
|
function isRouteFile(filePath) {
|
|
1441
1628
|
if (!state) return false;
|
|
1442
1629
|
const appDir = path3.resolve(state.options.root, state.options.appDir);
|
|
@@ -1445,6 +1632,25 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1445
1632
|
const nameWithoutExt = basename2.replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
1446
1633
|
return ROUTE_FILE_NAMES.includes(nameWithoutExt);
|
|
1447
1634
|
}
|
|
1635
|
+
function isQueueFile(filePath) {
|
|
1636
|
+
if (!state) return false;
|
|
1637
|
+
const queuesDir = path3.resolve(state.options.root, state.options.appDir, QUEUES_DIR);
|
|
1638
|
+
if (!filePath.startsWith(queuesDir)) return false;
|
|
1639
|
+
const relativePath = path3.relative(queuesDir, filePath);
|
|
1640
|
+
if (relativePath.includes(path3.sep)) return false;
|
|
1641
|
+
const ext = path3.extname(filePath);
|
|
1642
|
+
return state.options.config.extensions.includes(ext);
|
|
1643
|
+
}
|
|
1644
|
+
function isServiceFile(filePath) {
|
|
1645
|
+
if (!state) return false;
|
|
1646
|
+
const servicesDir = path3.resolve(state.options.root, state.options.appDir, SERVICES_DIR);
|
|
1647
|
+
if (!filePath.startsWith(servicesDir)) return false;
|
|
1648
|
+
const basename2 = path3.basename(filePath);
|
|
1649
|
+
const ext = path3.extname(filePath);
|
|
1650
|
+
const nameWithoutExt = basename2.replace(ext, "");
|
|
1651
|
+
if (nameWithoutExt !== SERVICE_FILE_NAME) return false;
|
|
1652
|
+
return state.options.config.extensions.includes(ext);
|
|
1653
|
+
}
|
|
1448
1654
|
function invalidateVirtualModules() {
|
|
1449
1655
|
if (!server) return;
|
|
1450
1656
|
const idsToInvalidate = [
|
|
@@ -1535,11 +1741,17 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1535
1741
|
errors: [],
|
|
1536
1742
|
notFound: []
|
|
1537
1743
|
},
|
|
1744
|
+
queueManifest: null,
|
|
1745
|
+
queueScanResult: null,
|
|
1746
|
+
serviceManifest: null,
|
|
1747
|
+
serviceScanResult: null,
|
|
1538
1748
|
clientComponents: /* @__PURE__ */ new Map(),
|
|
1539
1749
|
serverEntryCache: null,
|
|
1540
1750
|
clientEntryCache: null
|
|
1541
1751
|
};
|
|
1542
1752
|
await buildManifest(root);
|
|
1753
|
+
await buildQueueManifestIfExists(root);
|
|
1754
|
+
await buildServiceManifestIfExists(root);
|
|
1543
1755
|
await scanClientComponents(root, state);
|
|
1544
1756
|
},
|
|
1545
1757
|
/**
|
|
@@ -1559,6 +1771,22 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1559
1771
|
await buildManifest(state.options.root);
|
|
1560
1772
|
invalidateVirtualModules();
|
|
1561
1773
|
}
|
|
1774
|
+
if (isQueueFile(filePath)) {
|
|
1775
|
+
const queuesDir = path3.resolve(root, state.options.appDir, QUEUES_DIR);
|
|
1776
|
+
if (state?.options.verbose) {
|
|
1777
|
+
console.log(`[cloudwerk] Queue added: ${path3.relative(queuesDir, filePath)}`);
|
|
1778
|
+
}
|
|
1779
|
+
await buildQueueManifestIfExists(state.options.root);
|
|
1780
|
+
invalidateVirtualModules();
|
|
1781
|
+
}
|
|
1782
|
+
if (isServiceFile(filePath)) {
|
|
1783
|
+
const servicesDir = path3.resolve(root, state.options.appDir, SERVICES_DIR);
|
|
1784
|
+
if (state?.options.verbose) {
|
|
1785
|
+
console.log(`[cloudwerk] Service added: ${path3.relative(servicesDir, filePath)}`);
|
|
1786
|
+
}
|
|
1787
|
+
await buildServiceManifestIfExists(state.options.root);
|
|
1788
|
+
invalidateVirtualModules();
|
|
1789
|
+
}
|
|
1562
1790
|
});
|
|
1563
1791
|
devServer.watcher.on("unlink", async (filePath) => {
|
|
1564
1792
|
if (isRouteFile(filePath)) {
|
|
@@ -1568,6 +1796,22 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1568
1796
|
await buildManifest(state.options.root);
|
|
1569
1797
|
invalidateVirtualModules();
|
|
1570
1798
|
}
|
|
1799
|
+
if (isQueueFile(filePath)) {
|
|
1800
|
+
const queuesDir = path3.resolve(root, state.options.appDir, QUEUES_DIR);
|
|
1801
|
+
if (state?.options.verbose) {
|
|
1802
|
+
console.log(`[cloudwerk] Queue removed: ${path3.relative(queuesDir, filePath)}`);
|
|
1803
|
+
}
|
|
1804
|
+
await buildQueueManifestIfExists(state.options.root);
|
|
1805
|
+
invalidateVirtualModules();
|
|
1806
|
+
}
|
|
1807
|
+
if (isServiceFile(filePath)) {
|
|
1808
|
+
const servicesDir = path3.resolve(root, state.options.appDir, SERVICES_DIR);
|
|
1809
|
+
if (state?.options.verbose) {
|
|
1810
|
+
console.log(`[cloudwerk] Service removed: ${path3.relative(servicesDir, filePath)}`);
|
|
1811
|
+
}
|
|
1812
|
+
await buildServiceManifestIfExists(state.options.root);
|
|
1813
|
+
invalidateVirtualModules();
|
|
1814
|
+
}
|
|
1571
1815
|
});
|
|
1572
1816
|
devServer.watcher.on("change", async (filePath) => {
|
|
1573
1817
|
if (isRouteFile(filePath)) {
|
|
@@ -1577,6 +1821,22 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1577
1821
|
await buildManifest(state.options.root);
|
|
1578
1822
|
invalidateVirtualModules();
|
|
1579
1823
|
}
|
|
1824
|
+
if (isQueueFile(filePath)) {
|
|
1825
|
+
const queuesDir = path3.resolve(root, state.options.appDir, QUEUES_DIR);
|
|
1826
|
+
if (state?.options.verbose) {
|
|
1827
|
+
console.log(`[cloudwerk] Queue changed: ${path3.relative(queuesDir, filePath)}`);
|
|
1828
|
+
}
|
|
1829
|
+
await buildQueueManifestIfExists(state.options.root);
|
|
1830
|
+
invalidateVirtualModules();
|
|
1831
|
+
}
|
|
1832
|
+
if (isServiceFile(filePath)) {
|
|
1833
|
+
const servicesDir = path3.resolve(root, state.options.appDir, SERVICES_DIR);
|
|
1834
|
+
if (state?.options.verbose) {
|
|
1835
|
+
console.log(`[cloudwerk] Service changed: ${path3.relative(servicesDir, filePath)}`);
|
|
1836
|
+
}
|
|
1837
|
+
await buildServiceManifestIfExists(state.options.root);
|
|
1838
|
+
invalidateVirtualModules();
|
|
1839
|
+
}
|
|
1580
1840
|
const wranglerPath2 = findWranglerTomlPath(root);
|
|
1581
1841
|
if (wranglerPath2 && filePath === wranglerPath2) {
|
|
1582
1842
|
if (verbose) {
|
|
@@ -1621,7 +1881,11 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1621
1881
|
state.serverEntryCache = generateServerEntry(
|
|
1622
1882
|
state.manifest,
|
|
1623
1883
|
state.scanResult,
|
|
1624
|
-
state.options
|
|
1884
|
+
state.options,
|
|
1885
|
+
{
|
|
1886
|
+
queueManifest: state.queueManifest,
|
|
1887
|
+
serviceManifest: state.serviceManifest
|
|
1888
|
+
}
|
|
1625
1889
|
);
|
|
1626
1890
|
}
|
|
1627
1891
|
return state.serverEntryCache;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudwerk/vite-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Vite plugin for Cloudwerk file-based routing with virtual entry generation",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@swc/core": "^1.3.100",
|
|
22
|
-
"@cloudwerk/core": "^0.
|
|
23
|
-
"@cloudwerk/ui": "^0.
|
|
22
|
+
"@cloudwerk/core": "^0.13.0",
|
|
23
|
+
"@cloudwerk/ui": "^0.13.0"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"vite": "^5.0.0 || ^6.0.0",
|