@ensera/plugin-backend 1.0.0 → 1.1.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 CHANGED
@@ -1,4 +1,4 @@
1
- import { Request, Response, NextFunction } from 'express';
1
+ import { Request, Response, NextFunction, Express } from 'express';
2
2
 
3
3
  /**
4
4
  * Plugin scope extracted from JWT token
@@ -373,4 +373,22 @@ declare function toDateString(date: string | Date | null | undefined): string |
373
373
  */
374
374
  declare function buildDateTime(dateValue: string | Date | null | undefined, timeValue: string | null | undefined, defaultHour?: number, defaultMinute?: number): Date | null;
375
375
 
376
- export { type BackendNotification, type JsonObject, type JsonValue, type NotificationAction, type NotificationApiPayload, type NotificationBulkApiPayload, type NotificationBulkResponse, type NotificationCancelResponse, type NotificationCapabilities, type NotificationOptions, type NotificationPublishResponse, type NotificationPublisher, type NotificationPublisherConfig, type NotificationTypeConfig, type PluginAuthOptions, type PluginContext, PluginError, type PluginErrorResponse, type PluginScope, type RequestWithContext, type ScheduleOptions, assertPluginScope, buildDateTime, calculateScheduleTime, createNotificationPublisher, forbidClientInstanceId, formatTime, getPluginScope, isPastDate, pluginAuth, pluginErrorHandler, requireFeature, requirePermission, requireTabView, requireTaskScope, resolveEffectiveInstanceId, toDateString, withInstanceOnly, withInstanceScope, withUserAndInstance };
376
+ declare function withCoreServiceToken(handler: (req: Request, res: Response, next: NextFunction) => unknown): (req: Request, res: Response, next: NextFunction) => Promise<void>;
377
+
378
+ type CoreRequestContext = {
379
+ workspaceId: string;
380
+ spaceId: string;
381
+ userId: string;
382
+ taskScopeId?: string;
383
+ requestId: string;
384
+ };
385
+ type CommandHandler = (input: Record<string, unknown>, context: CoreRequestContext) => Promise<unknown>;
386
+ type CommandRegistry = {
387
+ register: (name: string, handler: CommandHandler) => void;
388
+ get: (name: string) => CommandHandler | undefined;
389
+ list: () => string[];
390
+ };
391
+ declare function createCommandRegistry(): CommandRegistry;
392
+ declare function createCoreRouter(app: Express, registry: CommandRegistry): void;
393
+
394
+ export { type BackendNotification, type CommandHandler, type CommandRegistry, type CoreRequestContext, type JsonObject, type JsonValue, type NotificationAction, type NotificationApiPayload, type NotificationBulkApiPayload, type NotificationBulkResponse, type NotificationCancelResponse, type NotificationCapabilities, type NotificationOptions, type NotificationPublishResponse, type NotificationPublisher, type NotificationPublisherConfig, type NotificationTypeConfig, type PluginAuthOptions, type PluginContext, PluginError, type PluginErrorResponse, type PluginScope, type RequestWithContext, type ScheduleOptions, assertPluginScope, buildDateTime, calculateScheduleTime, createCommandRegistry, createCoreRouter, createNotificationPublisher, forbidClientInstanceId, formatTime, getPluginScope, isPastDate, pluginAuth, pluginErrorHandler, requireFeature, requirePermission, requireTabView, requireTaskScope, resolveEffectiveInstanceId, toDateString, withCoreServiceToken, withInstanceOnly, withInstanceScope, withUserAndInstance };
package/dist/index.js CHANGED
@@ -657,11 +657,107 @@ function buildDateTime(dateValue, timeValue, defaultHour = 9, defaultMinute = 0)
657
657
  if (isNaN(result.getTime())) return null;
658
658
  return result;
659
659
  }
660
+
661
+ // src/withCoreServiceToken.ts
662
+ function withCoreServiceToken(handler) {
663
+ return async function coreServiceTokenWrapped(req, res, next) {
664
+ try {
665
+ const authHeader = req.headers.authorization;
666
+ const token = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;
667
+ const expected = process.env.CORE_SERVICE_TOKEN?.trim();
668
+ if (!expected) {
669
+ throw new PluginError({
670
+ status: 500,
671
+ code: "CORE_SERVICE_TOKEN_NOT_CONFIGURED",
672
+ message: "CORE_SERVICE_TOKEN is not set in environment"
673
+ });
674
+ }
675
+ if (!token || token !== expected) {
676
+ throw new PluginError({
677
+ status: 401,
678
+ code: "INVALID_CORE_SERVICE_TOKEN",
679
+ message: "Unauthorized: invalid or missing Core service token"
680
+ });
681
+ }
682
+ await handler(req, res, next);
683
+ } catch (e) {
684
+ next(e);
685
+ }
686
+ };
687
+ }
688
+
689
+ // src/createCommandRegistry.ts
690
+ function createCommandRegistry() {
691
+ const registry = /* @__PURE__ */ new Map();
692
+ return {
693
+ register(name, handler) {
694
+ if (!name || typeof handler !== "function") {
695
+ throw new Error(
696
+ "createCommandRegistry: name and handler are required"
697
+ );
698
+ }
699
+ registry.set(name, handler);
700
+ },
701
+ get(name) {
702
+ return registry.get(name);
703
+ },
704
+ list() {
705
+ return Array.from(registry.keys());
706
+ }
707
+ };
708
+ }
709
+ function createCoreRouter(app, registry) {
710
+ app.post(
711
+ "/api/core/execute",
712
+ withCoreServiceToken(async (req, res) => {
713
+ const { command, input, context } = req.body ?? {};
714
+ if (!command || typeof command !== "string") {
715
+ return res.status(400).json({
716
+ success: false,
717
+ error: "MISSING_COMMAND",
718
+ message: "Request body must include a command string"
719
+ });
720
+ }
721
+ if (!context?.workspaceId || !context?.userId) {
722
+ return res.status(400).json({
723
+ success: false,
724
+ error: "MISSING_CONTEXT",
725
+ message: "Request context must include workspaceId and userId"
726
+ });
727
+ }
728
+ const handler = registry.get(command);
729
+ if (!handler) {
730
+ return res.status(404).json({
731
+ success: false,
732
+ error: "UNKNOWN_COMMAND",
733
+ message: `Command '${command}' is not registered`,
734
+ available: registry.list()
735
+ });
736
+ }
737
+ try {
738
+ const result = await handler(
739
+ input ?? {},
740
+ context
741
+ );
742
+ return res.json({ success: true, data: result });
743
+ } catch (err) {
744
+ console.error(`[CoreRouter] Command '${command}' failed:`, err);
745
+ return res.status(500).json({
746
+ success: false,
747
+ error: "COMMAND_FAILED",
748
+ message: err?.message ?? "Internal error"
749
+ });
750
+ }
751
+ })
752
+ );
753
+ }
660
754
  export {
661
755
  PluginError,
662
756
  assertPluginScope,
663
757
  buildDateTime,
664
758
  calculateScheduleTime,
759
+ createCommandRegistry,
760
+ createCoreRouter,
665
761
  createNotificationPublisher,
666
762
  forbidClientInstanceId,
667
763
  formatTime,
@@ -675,6 +771,7 @@ export {
675
771
  requireTaskScope,
676
772
  resolveEffectiveInstanceId,
677
773
  toDateString,
774
+ withCoreServiceToken,
678
775
  withInstanceOnly,
679
776
  withInstanceScope,
680
777
  withUserAndInstance
package/package.json CHANGED
@@ -1,54 +1,54 @@
1
- {
2
- "name": "@ensera/plugin-backend",
3
- "version": "1.0.0",
4
- "description": "Runtime backend SDK for Ensera plugins.",
5
- "type": "module",
6
- "main": "./dist/index.js",
7
- "module": "./dist/index.js",
8
- "types": "./dist/index.d.ts",
9
- "files": [
10
- "dist",
11
- "README.md"
12
- ],
13
- "sideEffects": false,
14
- "scripts": {
15
- "build": "tsup src/index.ts --format esm --dts --clean --target es2022 --outDir dist",
16
- "dev": "tsup src/index.ts --format esm --dts --watch --target es2022 --outDir dist",
17
- "typecheck": "tsc -p tsconfig.json --noEmit",
18
- "prepublishOnly": "npm run build && npm run typecheck"
19
- },
20
- "keywords": [
21
- "ensera",
22
- "backend",
23
- "sdk",
24
- "plugin",
25
- "express"
26
- ],
27
- "publishConfig": {
28
- "access": "public"
29
- },
30
- "dependencies": {
31
- "jsonwebtoken": "^9.0.0"
32
- },
33
- "devDependencies": {
34
- "@types/express": "^4.17.0",
35
- "@types/jsonwebtoken": "^9.0.0",
36
- "tsup": "^8.0.0",
37
- "typescript": "^5.0.0",
38
- "jsonwebtoken": "^9.0.0"
39
- },
40
- "engines": {
41
- "node": ">=18"
42
- },
43
- "peerDependencies": {
44
- "express": "^4.18.0",
45
- "jsonwebtoken": "^9.0.0"
46
- },
47
- "exports": {
48
- ".": {
49
- "types": "./dist/index.d.ts",
50
- "import": "./dist/index.js"
51
- },
52
- "./package.json": "./package.json"
53
- }
54
- }
1
+ {
2
+ "name": "@ensera/plugin-backend",
3
+ "version": "1.1.0",
4
+ "description": "Runtime backend SDK for Ensera plugins.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
13
+ "sideEffects": false,
14
+ "scripts": {
15
+ "build": "tsup src/index.ts --format esm --dts --clean --target es2022 --outDir dist",
16
+ "dev": "tsup src/index.ts --format esm --dts --watch --target es2022 --outDir dist",
17
+ "typecheck": "tsc -p tsconfig.json --noEmit",
18
+ "prepublishOnly": "npm run build && npm run typecheck"
19
+ },
20
+ "keywords": [
21
+ "ensera",
22
+ "backend",
23
+ "sdk",
24
+ "plugin",
25
+ "express"
26
+ ],
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "dependencies": {
31
+ "jsonwebtoken": "^9.0.0"
32
+ },
33
+ "devDependencies": {
34
+ "@types/express": "^4.17.0",
35
+ "@types/jsonwebtoken": "^9.0.0",
36
+ "tsup": "^8.0.0",
37
+ "typescript": "^5.0.0",
38
+ "jsonwebtoken": "^9.0.0"
39
+ },
40
+ "engines": {
41
+ "node": ">=18"
42
+ },
43
+ "peerDependencies": {
44
+ "express": "^4.18.0",
45
+ "jsonwebtoken": "^9.0.0"
46
+ },
47
+ "exports": {
48
+ ".": {
49
+ "types": "./dist/index.d.ts",
50
+ "import": "./dist/index.js"
51
+ },
52
+ "./package.json": "./package.json"
53
+ }
54
+ }