@gmickel/gno 0.41.1 → 1.0.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.
@@ -91,6 +91,12 @@ import {
91
91
  import { searchHybrid } from "../../pipeline/hybrid";
92
92
  import { validateQueryModes } from "../../pipeline/query-modes";
93
93
  import { searchBm25 } from "../../pipeline/search";
94
+ import {
95
+ derivePublishArtifactFilename,
96
+ isPublishVisibility,
97
+ type PublishVisibility,
98
+ } from "../../publish/artifact";
99
+ import { exportPublishArtifact } from "../../publish/export-service";
94
100
  import { buildBrowseTree, normalizeBrowsePath } from "../browse-tree";
95
101
  import { applyConfigChange, applyConfigChangeTyped } from "../config-sync";
96
102
  import { getConnectorStatuses, installConnector } from "../connectors";
@@ -333,6 +339,15 @@ export interface CreateEditableCopyRequestBody {
333
339
  uri?: string;
334
340
  }
335
341
 
342
+ export interface PublishExportRequestBody {
343
+ encryptionPassphrase?: string;
344
+ slug?: string;
345
+ summary?: string;
346
+ target: string;
347
+ title?: string;
348
+ visibility?: PublishVisibility;
349
+ }
350
+
336
351
  // ─────────────────────────────────────────────────────────────────────────────
337
352
  // Helpers
338
353
  // ─────────────────────────────────────────────────────────────────────────────
@@ -726,6 +741,77 @@ export async function handleCollections(config: Config): Promise<Response> {
726
741
  );
727
742
  }
728
743
 
744
+ /**
745
+ * POST /api/publish/export
746
+ * Build a gno.sh-compatible publish artifact for a collection or single doc.
747
+ */
748
+ export async function handlePublishExport(
749
+ config: Config,
750
+ store: SqliteAdapter,
751
+ req: Request
752
+ ): Promise<Response> {
753
+ let body: PublishExportRequestBody;
754
+ try {
755
+ body = (await req.json()) as PublishExportRequestBody;
756
+ } catch {
757
+ return errorResponse("VALIDATION", "Invalid JSON body");
758
+ }
759
+
760
+ if (!body.target || typeof body.target !== "string") {
761
+ return errorResponse("VALIDATION", "Missing or invalid target");
762
+ }
763
+ if (body.slug !== undefined && typeof body.slug !== "string") {
764
+ return errorResponse("VALIDATION", "slug must be a string");
765
+ }
766
+ if (body.summary !== undefined && typeof body.summary !== "string") {
767
+ return errorResponse("VALIDATION", "summary must be a string");
768
+ }
769
+ if (
770
+ body.encryptionPassphrase !== undefined &&
771
+ typeof body.encryptionPassphrase !== "string"
772
+ ) {
773
+ return errorResponse("VALIDATION", "encryptionPassphrase must be a string");
774
+ }
775
+ if (body.title !== undefined && typeof body.title !== "string") {
776
+ return errorResponse("VALIDATION", "title must be a string");
777
+ }
778
+ if (body.visibility !== undefined && !isPublishVisibility(body.visibility)) {
779
+ return errorResponse(
780
+ "VALIDATION",
781
+ "visibility must be public, secret-link, invite-only, or encrypted"
782
+ );
783
+ }
784
+
785
+ try {
786
+ const artifact = await exportPublishArtifact({
787
+ collections: config.collections,
788
+ options: {
789
+ encryptionPassphrase: body.encryptionPassphrase,
790
+ routeSlug: body.slug,
791
+ summary: body.summary,
792
+ title: body.title,
793
+ visibility: body.visibility,
794
+ },
795
+ store,
796
+ target: body.target.trim(),
797
+ });
798
+
799
+ return jsonResponse({
800
+ artifact,
801
+ fileName: derivePublishArtifactFilename(artifact),
802
+ uploadUrl: "https://gno.sh/studio",
803
+ });
804
+ } catch (error) {
805
+ return errorResponse(
806
+ "RUNTIME",
807
+ error instanceof Error
808
+ ? error.message
809
+ : "Failed to export publish artifact",
810
+ 500
811
+ );
812
+ }
813
+ }
814
+
729
815
  /**
730
816
  * POST /api/collections
731
817
  * Create a new collection and start sync job.
@@ -3894,6 +3980,10 @@ export async function routeApi(
3894
3980
  return handleCollections(config);
3895
3981
  }
3896
3982
 
3983
+ if (path === "/api/publish/export" && req.method === "POST") {
3984
+ return handlePublishExport(config, store, req);
3985
+ }
3986
+
3897
3987
  if (path === "/api/docs") {
3898
3988
  return handleDocs(store, url);
3899
3989
  }
@@ -42,6 +42,7 @@ import {
42
42
  handleJob,
43
43
  handleModelPull,
44
44
  handleModelStatus,
45
+ handlePublishExport,
45
46
  handlePresets,
46
47
  handleQuery,
47
48
  handleRefactorPlan,
@@ -234,6 +235,17 @@ export async function startServer(
234
235
  );
235
236
  },
236
237
  },
238
+ "/api/publish/export": {
239
+ POST: async (req: Request) => {
240
+ if (!isRequestAllowed(req, port)) {
241
+ return withSecurityHeaders(forbiddenResponse(), isDev);
242
+ }
243
+ return withSecurityHeaders(
244
+ await handlePublishExport(ctxHolder.config, store, req),
245
+ isDev
246
+ );
247
+ },
248
+ },
237
249
  "/api/sync": {
238
250
  POST: async (req: Request) => {
239
251
  if (!isRequestAllowed(req, port)) {