@ensera/plugin-backend 1.1.0 → 1.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.
package/dist/index.d.ts CHANGED
@@ -391,4 +391,17 @@ type CommandRegistry = {
391
391
  declare function createCommandRegistry(): CommandRegistry;
392
392
  declare function createCoreRouter(app: Express, registry: CommandRegistry): void;
393
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 };
394
+ type CreateDbOptions = {
395
+ prismaDir?: string;
396
+ coreApiUrl?: string;
397
+ serviceToken?: string;
398
+ runMigrations?: boolean;
399
+ };
400
+ type DbProvisionResult = {
401
+ connectionString: string;
402
+ schemaName: string;
403
+ featureSlug: string;
404
+ };
405
+ declare function provisionDb(options?: CreateDbOptions): Promise<DbProvisionResult>;
406
+
407
+ export { type BackendNotification, type CommandHandler, type CommandRegistry, type CoreRequestContext, type CreateDbOptions, type DbProvisionResult, 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, provisionDb, requireFeature, requirePermission, requireTabView, requireTaskScope, resolveEffectiveInstanceId, toDateString, withCoreServiceToken, withInstanceOnly, withInstanceScope, withUserAndInstance };
package/dist/index.js CHANGED
@@ -751,6 +751,108 @@ function createCoreRouter(app, registry) {
751
751
  })
752
752
  );
753
753
  }
754
+
755
+ // src/createDb.ts
756
+ import { execSync } from "child_process";
757
+ import fs from "fs";
758
+ import path from "path";
759
+ function isRecord(value) {
760
+ return typeof value === "object" && value !== null && !Array.isArray(value);
761
+ }
762
+ function getString(value) {
763
+ if (typeof value !== "string") return void 0;
764
+ const trimmed = value.trim();
765
+ return trimmed.length > 0 ? trimmed : void 0;
766
+ }
767
+ function parseProvisionResult(value) {
768
+ if (!isRecord(value)) {
769
+ throw new Error("provisionDb: Core returned an invalid response body");
770
+ }
771
+ const connectionString = getString(value.connectionString);
772
+ const schemaName = getString(value.schemaName);
773
+ const featureSlug = getString(value.featureSlug);
774
+ if (!connectionString) {
775
+ throw new Error("provisionDb: Core did not return a connectionString");
776
+ }
777
+ if (!schemaName) {
778
+ throw new Error("provisionDb: Core did not return a schemaName");
779
+ }
780
+ if (!featureSlug) {
781
+ throw new Error("provisionDb: Core did not return a featureSlug");
782
+ }
783
+ return {
784
+ connectionString,
785
+ schemaName,
786
+ featureSlug
787
+ };
788
+ }
789
+ async function provisionDb(options = {}) {
790
+ const coreApiUrl = (options.coreApiUrl ?? process.env.CORE_API_URL)?.replace(/\/+$/, "");
791
+ const serviceToken = options.serviceToken ?? process.env.CORE_SERVICE_TOKEN;
792
+ if (!coreApiUrl) {
793
+ throw new Error(
794
+ "provisionDb: CORE_API_URL is not set. Add it to your .env file."
795
+ );
796
+ }
797
+ if (!serviceToken) {
798
+ throw new Error(
799
+ "provisionDb: CORE_SERVICE_TOKEN is not set. Add it to your .env file."
800
+ );
801
+ }
802
+ const response = await fetch(`${coreApiUrl}/api/internal/db/provision`, {
803
+ method: "POST",
804
+ headers: {
805
+ "Content-Type": "application/json",
806
+ Authorization: `Bearer ${serviceToken}`
807
+ }
808
+ });
809
+ let payload = null;
810
+ try {
811
+ payload = await response.json();
812
+ } catch {
813
+ payload = null;
814
+ }
815
+ if (!response.ok) {
816
+ const errorMessage = isRecord(payload) ? getString(payload.error) : void 0;
817
+ throw new Error(
818
+ `provisionDb: Core returned ${response.status}. ${errorMessage ?? "Unknown error"}`
819
+ );
820
+ }
821
+ const result = parseProvisionResult(payload);
822
+ const shouldMigrate = options.runMigrations !== false;
823
+ if (shouldMigrate) {
824
+ const prismaDir = options.prismaDir ?? path.join(process.cwd(), "prisma");
825
+ const migrationsDir = path.join(prismaDir, "migrations");
826
+ if (fs.existsSync(prismaDir) && fs.existsSync(migrationsDir)) {
827
+ console.log(
828
+ `[ensera-db] Running migrations for schema: ${result.schemaName}`
829
+ );
830
+ try {
831
+ execSync("npx prisma migrate deploy", {
832
+ cwd: path.dirname(prismaDir),
833
+ stdio: "inherit",
834
+ env: {
835
+ ...process.env,
836
+ DATABASE_URL: result.connectionString
837
+ }
838
+ });
839
+ console.log("[ensera-db] Migrations complete");
840
+ } catch (error) {
841
+ const message = error instanceof Error ? error.message : "Unknown migration error";
842
+ throw new Error(`[ensera-db] Migration failed: ${message}`);
843
+ }
844
+ } else if (fs.existsSync(prismaDir)) {
845
+ console.warn(
846
+ `[ensera-db] No prisma migrations found at ${migrationsDir}. Skipping migrations.`
847
+ );
848
+ } else {
849
+ console.warn(
850
+ `[ensera-db] No prisma directory found at ${prismaDir}. Skipping migrations.`
851
+ );
852
+ }
853
+ }
854
+ return result;
855
+ }
754
856
  export {
755
857
  PluginError,
756
858
  assertPluginScope,
@@ -765,6 +867,7 @@ export {
765
867
  isPastDate,
766
868
  pluginAuth,
767
869
  pluginErrorHandler,
870
+ provisionDb,
768
871
  requireFeature,
769
872
  requirePermission,
770
873
  requireTabView,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ensera/plugin-backend",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Runtime backend SDK for Ensera plugins.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",