@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.
- package/README.md +68 -1
- package/assets/screenshots/publish-reader.jpg +0 -0
- package/assets/skill/SKILL.md +2 -0
- package/assets/skill/cli-reference.md +28 -0
- package/package.json +1 -1
- package/src/cli/commands/index.ts +6 -0
- package/src/cli/commands/publish.ts +142 -0
- package/src/cli/options.ts +2 -0
- package/src/cli/program.ts +85 -0
- package/src/publish/artifact.ts +297 -0
- package/src/publish/encrypted-export.ts +384 -0
- package/src/publish/export-service.ts +305 -0
- package/src/serve/AGENTS.md +17 -16
- package/src/serve/CLAUDE.md +17 -16
- package/src/serve/public/lib/publish-export.ts +21 -0
- package/src/serve/public/pages/Collections.tsx +63 -0
- package/src/serve/public/pages/DocView.tsx +71 -0
- package/src/serve/routes/api.ts +90 -0
- package/src/serve/server.ts +12 -0
package/src/serve/routes/api.ts
CHANGED
|
@@ -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
|
}
|
package/src/serve/server.ts
CHANGED
|
@@ -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)) {
|