dineway 0.1.48 → 0.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/{activity-events-C9u37D5z.mjs → activity-events-y6eT8UW6.mjs} +6 -38
- package/dist/actor-CrObAPV0.mjs +37 -0
- package/dist/after-BfniwgIf.mjs +9 -0
- package/dist/{allowed-origins-DhipQNLb.mjs → allowed-origins-_oeyP6x-.mjs} +1 -1
- package/dist/api/route-utils.d.mts +10 -10
- package/dist/api/route-utils.mjs +17 -14
- package/dist/api/schemas/index.d.mts +2 -2
- package/dist/api/schemas/index.mjs +5 -4
- package/dist/{api-Eiy8_zVZ.mjs → api-ByrU2q-Z.mjs} +61 -37
- package/dist/{api-tokens-DtvdtIkm.mjs → api-tokens-CjVMQT-X.mjs} +6 -2
- package/dist/{apply-CX3no7XE.mjs → apply-bZswmZPm.mjs} +117 -77
- package/dist/astro/image-endpoint.d.mts +7 -0
- package/dist/astro/image-endpoint.mjs +54 -0
- package/dist/astro/index.d.mts +35 -11
- package/dist/astro/index.mjs +157 -23
- package/dist/astro/integration/shims/use-sync-external-store-with-selector.js +71 -0
- package/dist/astro/integration/shims/use-sync-external-store.js +1 -0
- package/dist/astro/middleware/auth.d.mts +10 -10
- package/dist/astro/middleware/auth.mjs +42 -24
- package/dist/astro/middleware/redirect.mjs +14 -5
- package/dist/astro/middleware/request-context.mjs +9 -6
- package/dist/astro/middleware/seed.d.mts +1 -1
- package/dist/astro/middleware/seed.mjs +2 -2
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.mjs +334 -193
- package/dist/astro/routes/admin.astro +6 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +7 -6
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +7 -6
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +4 -4
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +6 -6
- package/dist/astro/routes/api/admin/briefing.mjs +22 -18
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +4 -4
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +9 -8
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +9 -8
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +9 -8
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +18 -15
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +18 -15
- package/dist/astro/routes/api/admin/bylines/index.mjs +18 -15
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +15 -13
- package/dist/astro/routes/api/admin/comments/_id_.mjs +7 -6
- package/dist/astro/routes/api/admin/comments/bulk.mjs +12 -10
- package/dist/astro/routes/api/admin/comments/counts.mjs +7 -6
- package/dist/astro/routes/api/admin/comments/index.mjs +12 -10
- package/dist/astro/routes/api/admin/context/_id_/history.mjs +27 -25
- package/dist/astro/routes/api/admin/context/_id_/index.mjs +27 -25
- package/dist/astro/routes/api/admin/context/_id_/review.mjs +32 -29
- package/dist/astro/routes/api/admin/context/_id_/supersede.mjs +32 -29
- package/dist/astro/routes/api/admin/context/diff.mjs +31 -28
- package/dist/astro/routes/api/admin/context/index.mjs +32 -29
- package/dist/astro/routes/api/admin/context/stale.mjs +31 -28
- package/dist/astro/routes/api/admin/hitl-requests/_id_/index.mjs +27 -25
- package/dist/astro/routes/api/admin/hitl-requests/_id_/resolve.mjs +31 -28
- package/dist/astro/routes/api/admin/hitl-requests/index.mjs +31 -28
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +28 -26
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +6 -6
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +6 -6
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +56 -52
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +56 -52
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +48 -44
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +54 -50
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +53 -49
- package/dist/astro/routes/api/admin/plugins/index.mjs +48 -44
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +2 -2
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +47 -43
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +53 -49
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +47 -43
- package/dist/astro/routes/api/admin/plugins/updates.mjs +47 -43
- package/dist/astro/routes/api/admin/review-requests/_id_/index.mjs +6 -6
- package/dist/astro/routes/api/admin/review-requests/_id_/resolve.mjs +32 -28
- package/dist/astro/routes/api/admin/review-requests/index.mjs +11 -10
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +47 -43
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +2 -2
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +47 -43
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +3 -3
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +8 -7
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +4 -4
- package/dist/astro/routes/api/admin/users/index.mjs +7 -6
- package/dist/astro/routes/api/auth/dev-bypass.mjs +4 -4
- package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
- package/dist/astro/routes/api/auth/invite/complete.mjs +12 -11
- package/dist/astro/routes/api/auth/invite/index.mjs +9 -8
- package/dist/astro/routes/api/auth/invite/register-options.mjs +11 -10
- package/dist/astro/routes/api/auth/logout.mjs +2 -2
- package/dist/astro/routes/api/auth/magic-link/send.mjs +11 -10
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +3 -3
- package/dist/astro/routes/api/auth/me.mjs +10 -9
- package/dist/astro/routes/api/auth/mode.mjs +1 -1
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +3 -3
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +6 -5
- package/dist/astro/routes/api/auth/passkey/index.mjs +1 -1
- package/dist/astro/routes/api/auth/passkey/options.mjs +12 -11
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +10 -9
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +11 -10
- package/dist/astro/routes/api/auth/passkey/verify.mjs +11 -10
- package/dist/astro/routes/api/auth/setup-token/index.mjs +6 -6
- package/dist/astro/routes/api/auth/setup-token/verify.mjs +2 -2
- package/dist/astro/routes/api/auth/signup/complete.mjs +12 -11
- package/dist/astro/routes/api/auth/signup/request.mjs +11 -10
- package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +16 -14
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.d.mts +8 -0
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +160 -0
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +12 -11
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +11 -10
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +11 -10
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +17 -15
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +11 -10
- package/dist/astro/routes/api/content/_collection_/authors.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/index.mjs +11 -10
- package/dist/astro/routes/api/content/_collection_/trash.mjs +7 -6
- package/dist/astro/routes/api/dashboard.mjs +10 -9
- package/dist/astro/routes/api/dev/emails.mjs +2 -2
- package/dist/astro/routes/api/health.mjs +1 -1
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +23 -11
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +9 -6
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +19 -1
- package/dist/astro/routes/api/import/wordpress/execute.mjs +85 -38
- package/dist/astro/routes/api/import/wordpress/media.d.mts +13 -1
- package/dist/astro/routes/api/import/wordpress/media.mjs +20 -14
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +36 -33
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +18 -1
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +121 -101
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +25 -13
- package/dist/astro/routes/api/import/wordpress-plugin/callback.mjs +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +56 -2
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +284 -56
- package/dist/astro/routes/api/manifest.mjs +14 -5
- package/dist/astro/routes/api/mcp.mjs +266 -70
- package/dist/astro/routes/api/media/_id_/confirm.mjs +39 -11
- package/dist/astro/routes/api/media/_id_.mjs +11 -7
- package/dist/astro/routes/api/media/file/_...key_.mjs +1 -1
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +2 -2
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +2 -2
- package/dist/astro/routes/api/media/providers/index.mjs +2 -2
- package/dist/astro/routes/api/media/upload-url.mjs +12 -11
- package/dist/astro/routes/api/media.mjs +28 -35
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +32 -29
- package/dist/astro/routes/api/menus/_name_/items.mjs +32 -29
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +32 -29
- package/dist/astro/routes/api/menus/_name_/translations.mjs +32 -29
- package/dist/astro/routes/api/menus/_name_.mjs +32 -29
- package/dist/astro/routes/api/menus/index.mjs +32 -29
- package/dist/astro/routes/api/oauth/authorize.mjs +7 -7
- package/dist/astro/routes/api/oauth/device/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/code.mjs +9 -9
- package/dist/astro/routes/api/oauth/device/token.mjs +8 -8
- package/dist/astro/routes/api/oauth/register.mjs +4 -4
- package/dist/astro/routes/api/oauth/token/refresh.mjs +6 -6
- package/dist/astro/routes/api/oauth/token/revoke.mjs +6 -6
- package/dist/astro/routes/api/oauth/token.mjs +6 -6
- package/dist/astro/routes/api/openapi.json.mjs +4 -3
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +30 -30
- package/dist/astro/routes/api/redirects/404s/index.mjs +12 -11
- package/dist/astro/routes/api/redirects/404s/summary.mjs +12 -11
- package/dist/astro/routes/api/redirects/_id_.mjs +33 -30
- package/dist/astro/routes/api/redirects/index.mjs +33 -30
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +2 -2
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +2 -2
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +48 -44
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +48 -44
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +48 -44
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +48 -44
- package/dist/astro/routes/api/schema/collections/index.mjs +48 -44
- package/dist/astro/routes/api/schema/index.mjs +13 -14
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +47 -43
- package/dist/astro/routes/api/schema/orphans/index.mjs +47 -43
- package/dist/astro/routes/api/search/enable.mjs +12 -11
- package/dist/astro/routes/api/search/index.mjs +11 -10
- package/dist/astro/routes/api/search/rebuild.mjs +12 -11
- package/dist/astro/routes/api/search/stats.mjs +7 -7
- package/dist/astro/routes/api/search/suggest.mjs +11 -10
- package/dist/astro/routes/api/sections/_slug_.mjs +32 -29
- package/dist/astro/routes/api/sections/index.mjs +32 -29
- package/dist/astro/routes/api/settings/email.mjs +4 -4
- package/dist/astro/routes/api/settings.mjs +32 -29
- package/dist/astro/routes/api/setup/admin-verify.mjs +14 -13
- package/dist/astro/routes/api/setup/admin.mjs +12 -11
- package/dist/astro/routes/api/setup/dev-bypass.mjs +32 -26
- package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
- package/dist/astro/routes/api/setup/index.mjs +30 -26
- package/dist/astro/routes/api/setup/status.mjs +3 -3
- package/dist/astro/routes/api/snapshot.mjs +9 -8
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +35 -32
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +35 -32
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +35 -32
- package/dist/astro/routes/api/taxonomies/index.mjs +35 -32
- package/dist/astro/routes/api/themes/preview.mjs +6 -6
- package/dist/astro/routes/api/typegen.mjs +4 -4
- package/dist/astro/routes/api/well-known/auth.mjs +3 -3
- package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +3 -3
- package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +3 -3
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +31 -28
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +32 -29
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +32 -29
- package/dist/astro/routes/api/widget-areas/_name_.mjs +29 -27
- package/dist/astro/routes/api/widget-areas/index.mjs +32 -29
- package/dist/astro/routes/api/widget-components.mjs +2 -2
- package/dist/astro/routes/robots.txt.mjs +9 -8
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +24 -15
- package/dist/astro/routes/sitemap.xml.mjs +10 -9
- package/dist/astro/session-driver.d.mts +1 -1
- package/dist/astro/session-driver.mjs +3 -3
- package/dist/astro/types.d.mts +21 -13
- package/dist/{audit-D1cbG7xH.mjs → audit-ByVKIwWF.mjs} +1 -1
- package/dist/auth/providers/github.d.mts +1 -1
- package/dist/auth/providers/google.d.mts +1 -1
- package/dist/{auth-control-guard-Dr14I98m.mjs → auth-control-guard-CqeUmQsv.mjs} +1 -1
- package/dist/{authorize-BLO0m7fW.mjs → authorize-CcBxcHtz.mjs} +1 -1
- package/dist/{briefing-CObXGOGz.mjs → briefing-BJaJI8dm.mjs} +2 -1
- package/dist/{briefing-gKWiGLt1.mjs → briefing-CawZ59CY.mjs} +90 -28
- package/dist/bundle-7C_DpkEq.mjs +602 -0
- package/dist/{byline-1ntDwv8c.mjs → byline-e51-fWRs.mjs} +30 -62
- package/dist/{byline-fields-BX7rWKNU.d.mts → byline-fields-CUvbslGc.d.mts} +49 -6
- package/dist/{byline-fields-C9_iaPaM.mjs → byline-fields-D-N5qVIG.mjs} +1 -1
- package/dist/{byline-fields-B-ZkoVft.mjs → byline-fields-DOJO16Gm.mjs} +3 -3
- package/dist/{byline-registry-CUSvkasJ.mjs → byline-registry-87nNnYl4.mjs} +2 -2
- package/dist/{bylines-bC15ZqAv.mjs → bylines-CMQNCNFh.mjs} +9 -3
- package/dist/{bylines-DtxChhN_.mjs → bylines-y4LO-dTj.mjs} +5 -5
- package/dist/{cache-DbKyr60Q.mjs → cache-C-FNTFYK.mjs} +2 -2
- package/dist/{chunks-C5zp2xir.mjs → chunks-CxBt-GxR.mjs} +1 -1
- package/dist/cli/index.mjs +820 -1276
- package/dist/client/external-auth-headers.d.mts +1 -1
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.mjs +2 -1
- package/dist/{comment-CXDOQ93N.mjs → comment-DlA4iChg.mjs} +13 -3
- package/dist/comment-reaction-DfFao1ST.mjs +58 -0
- package/dist/{comments-hK-rdzJ9.mjs → comments-DApFGMsP.mjs} +1 -1
- package/dist/{content-QaryevFO.mjs → content-Bh4BDjv8.mjs} +51 -34
- package/dist/content-refresh-CgutSUfO.mjs +1576 -0
- package/dist/{context-CoYb_fvK.mjs → context-C5W3G18y.mjs} +111 -50
- package/dist/{context-DBL1ib4v.mjs → context-CK4ym2Sf.mjs} +2 -2
- package/dist/{context-route-helpers-CYQVmbIm.mjs → context-route-helpers-MASyGgWf.mjs} +2 -1
- package/dist/{cron-DxCAU_Sn.mjs → cron-cKcNTix_.mjs} +7 -3
- package/dist/{dashboard-DNlqsknJ.mjs → dashboard-DQkVT7Ei.mjs} +4 -4
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +1 -1
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/{device-flow-DygsFPGL.mjs → device-flow-BnqRH-T9.mjs} +5 -5
- package/dist/{placeholder-BDo81Is5.mjs → enrich-DIFIFA8O.mjs} +36 -9
- package/dist/{error-DCSuUAZW.mjs → error-CytFlgcd.mjs} +5 -0
- package/dist/field-defs-cache-DRNdhBJv.mjs +49 -0
- package/dist/{fts-manager-CIZlScg5.mjs → fts-manager-Crzypjqu.mjs} +1 -1
- package/dist/hitl-Hw1RmWOq.mjs +677 -0
- package/dist/{hitl-requests-CGQwOEiq.mjs → hitl-requests-B4Vbcmaq.mjs} +2 -2
- package/dist/{hitl-route-helpers-GSDLNPpN.mjs → hitl-route-helpers-hEVzvP8l.mjs} +5 -4
- package/dist/import-Bh9biJfx.mjs +1354 -0
- package/dist/{index-CwRuZ-u8.d.mts → index-D4y6NGiy.d.mts} +75 -3
- package/dist/index.d.mts +17 -16
- package/dist/index.mjs +81 -69
- package/dist/{jsonld-DZ67souc.d.mts → jsonld-Dq0gsnOH.d.mts} +8 -4
- package/dist/{load-xE01Jm0V.mjs → load-DmYeniXl.mjs} +1 -1
- package/dist/{loader-tg63WSY2.mjs → loader-CjWqjweO.mjs} +117 -31
- package/dist/{manifest-schema-qh2P7RDX.mjs → manifest-schema-BxVPYMJR.mjs} +34 -1
- package/dist/mcp-reviewed-executor-TwVbMzG6.mjs +555 -0
- package/dist/media/image-endpoint.d.mts +32 -0
- package/dist/media/image-endpoint.mjs +111 -0
- package/dist/media/index.d.mts +27 -2
- package/dist/media/index.mjs +5 -3
- package/dist/media/local-runtime.d.mts +16 -11
- package/dist/media/local-runtime.mjs +10 -8
- package/dist/{media-DUAjMfvR.mjs → media-DMj0grzC.mjs} +3 -1
- package/dist/{media-allowlist-BsXkT9RK.mjs → media-allowlist-BlVCSzHx.mjs} +1 -1
- package/dist/media-url-lEKSrpr9.mjs +20 -0
- package/dist/{menus-WpFRMXjz.mjs → menus-TaW1gKnk.mjs} +19 -5
- package/dist/{menus-DU1eZqw_.mjs → menus-xvsnVZXw.mjs} +80 -15
- package/dist/{normalize-DUnoXAa9.mjs → normalize-5ek_z7de.mjs} +8 -1
- package/dist/{oauth-authorization-D-1p52Je.mjs → oauth-authorization-DiaGvRVz.mjs} +5 -5
- package/dist/{oauth-clients-CzeMGyUU.mjs → oauth-clients-Dl9E8dEM.mjs} +2 -2
- package/dist/object-cache/memory.d.mts +14 -0
- package/dist/object-cache/memory.mjs +55 -0
- package/dist/object-cache-D51qOseS.mjs +419 -0
- package/dist/page/index.d.mts +4 -3
- package/dist/page/index.mjs +2 -2
- package/dist/{parse-CneSgl7-.mjs → parse-CbymENaR.mjs} +1 -1
- package/dist/{parser-DxfsARXL.mjs → parser-C1nYd-81.mjs} +151 -9
- package/dist/{placeholder-CXfoCtCd.d.mts → placeholder-uvNRZBX9.d.mts} +12 -4
- package/dist/plugin-utils.d.mts +9 -9
- package/dist/plugins/adapt-sandbox-entry.d.mts +9 -9
- package/dist/plugins/adapt-sandbox-entry.mjs +11 -2
- package/dist/{plugins-C57YQi4l.mjs → plugins-BolVa-tl.mjs} +832 -64
- package/dist/{transport-LSdZVXHF.mjs → portable-text-dA_UlJwW.mjs} +1 -134
- package/dist/{preview-D5rvDhRt.mjs → preview-vSVg6fwG.mjs} +4 -3
- package/dist/process-cache-7xgAO9JY.mjs +106 -0
- package/dist/{provider-loader-9it4vd6L.d.mts → provider-loader-MD8zAbnH.d.mts} +1 -1
- package/dist/{public-url-DLUM5OOv.mjs → public-url-BEz_sM9n.mjs} +24 -18
- package/dist/{public-url-B5HaoGzj.d.mts → public-url-gsp3oUbu.d.mts} +5 -4
- package/dist/{query-BYc3oruA.mjs → query-CfRCdaL3.mjs} +281 -76
- package/dist/{query-DKEwzobh.mjs → query-jaAmw8E1.mjs} +11 -6
- package/dist/{rate-limit-i0G3IAOX.mjs → rate-limit-ZLdyWoZF.mjs} +2 -2
- package/dist/{redirect-C2zXRj3c.mjs → redirect-BAjt4Z6b.mjs} +2 -2
- package/dist/{redirects-YZo_znWG.mjs → redirects-BTdwpQtE.mjs} +47 -24
- package/dist/{redirects-ocRJmQHP.mjs → redirects-CA-xylPG.mjs} +19 -11
- package/dist/{registry-CTkbIDB9.mjs → registry-CtyVedcN.mjs} +130 -77
- package/dist/request-context.d.mts +2 -0
- package/dist/{request-meta-DAb6xaP9.mjs → request-meta-CUypToDJ.mjs} +1 -1
- package/dist/{review-requests-C0un5kdV.mjs → review-requests-C9Ff11Vq.mjs} +1 -1
- package/dist/{review-requests-gGMyYYhy.mjs → review-requests-DUyEmtmG.mjs} +1 -1
- package/dist/{runner-D4FL9usy.d.mts → runner-1tC2tIRV.d.mts} +1 -1
- package/dist/runner-DB7CRfOF.mjs +6740 -0
- package/dist/{runtime-DEWQpZ-4.d.mts → runtime-BOeiQsCv.d.mts} +2028 -1564
- package/dist/runtime.d.mts +10 -10
- package/dist/runtime.mjs +4 -4
- package/dist/schema-DXdYj4R3.mjs +8 -0
- package/dist/{search-axGQ1KY3.mjs → search-DSaLgEFI.mjs} +5 -3
- package/dist/{secrets-062Izo8i.mjs → secrets-CDZo2k8L.mjs} +15 -12
- package/dist/{sections-OLKgXL10.mjs → sections-PVVXHGfp.mjs} +2 -2
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +22 -19
- package/dist/seo/index.d.mts +1 -1
- package/dist/seo/index.mjs +3 -12
- package/dist/{seo-Cd45Po8L.mjs → seo-CQG3GCHQ.mjs} +5 -2
- package/dist/{seo-contributions-CqVvReEj.mjs → seo-contributions-C1EnikHB.mjs} +11 -3
- package/dist/{seo-5lJOtB5J.mjs → seo-qa7Dn0jK.mjs} +22 -4
- package/dist/{service-Bwad8s5h.mjs → service-DiN4ZuPp.mjs} +2 -2
- package/dist/session-user-CAKImna3.mjs +22 -0
- package/dist/{settings-yLjz_I4T.mjs → settings-BbqgHWpU.mjs} +28 -55
- package/dist/{settings-lAHjaUit.mjs → settings-i4VPP8bT.mjs} +2 -2
- package/dist/{setup-complete--PradA4R.mjs → setup-complete-B8fl-mjV.mjs} +1 -1
- package/dist/{site-activity-D7Xyropw.mjs → site-activity-Dy3FROs0.mjs} +1 -1
- package/dist/{site-context-DaN9Z91j.mjs → site-context-DQiL8Fl5.mjs} +23 -652
- package/dist/{site-url-DpGZo9Z6.mjs → site-url-BML3GQYm.mjs} +1 -1
- package/dist/status-DANd8ERo.mjs +21 -0
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/s3.d.mts +1 -1
- package/dist/storage/s3.mjs +1 -1
- package/dist/{taxonomies-uXVEoQso.mjs → taxonomies-9s-v03H-.mjs} +36 -29
- package/dist/taxonomies-DRiixp_E.mjs +443 -0
- package/dist/{taxonomy-CBmkmMuU.mjs → taxonomy-Dvsw6BOs.mjs} +44 -9
- package/dist/{tokens-CjSgb0AC.mjs → tokens-1RK1n6ED.mjs} +1 -1
- package/dist/transport-BNmCqLu7.mjs +134 -0
- package/dist/types-4garmdWa.mjs +66 -0
- package/dist/{types-DSPGWjxO.d.mts → types-C8XW6L19.d.mts} +225 -4
- package/dist/{types-GaCtZ1k9.d.mts → types-CDpH6MA4.d.mts} +18 -0
- package/dist/{types-DRIKLmJI.d.mts → types-C_ly5eTp.d.mts} +17 -2
- package/dist/types-DF7ZuGGk.d.mts +118 -0
- package/dist/types-DU0bU2Wu.d.mts +2392 -0
- package/dist/{types-CwawBUTa.d.mts → types-DW_-sFyz.d.mts} +2 -1
- package/dist/{types-DfuD0Afl.mjs → types-Dq1qT19x.mjs} +2 -2
- package/dist/ui/server-runtime.d.mts +15 -15
- package/dist/ui/server-runtime.mjs +25 -21
- package/dist/url-Cac7a9QZ.mjs +33 -0
- package/dist/{user-DtNmjNYt.mjs → user-BIZPzCb9.mjs} +2 -2
- package/dist/{utils-wv-zvC-Y.mjs → utils-CMDhr5Xd.mjs} +129 -7
- package/dist/{validate-BMs11tan.d.mts → validate-CZvb-BKB.d.mts} +10 -7
- package/dist/{validate-7SYRbz2f.mjs → validate-DNs0pTSc.mjs} +11 -6
- package/dist/version-BTvfRGc6.mjs +6 -0
- package/dist/{widgets-DR8EHuRO.mjs → widgets-f0ibZUIq.mjs} +46 -43
- package/dist/{wordpress-slugs-Dvhxh4cl.mjs → wordpress-slugs-BhHJX8XE.mjs} +1 -1
- package/dist/{zod-generator-BS9-umbk.mjs → zod-generator-B7TMjyFJ.mjs} +41 -13
- package/package.json +31 -8
- package/src/components/Block.astro +50 -0
- package/src/components/Comments.astro +143 -1
- package/src/components/DinewayHead.astro +19 -2
- package/src/components/DinewayImage.astro +66 -33
- package/src/components/DinewayMedia.astro +1 -2
- package/src/components/Image.astro +85 -40
- package/src/components/InlinePortableTextEditor.tsx +12 -2
- package/src/components/JsonLdScript.astro +9 -0
- package/src/components/LiveSearch.astro +40 -4
- package/src/components/index.ts +4 -0
- package/src/components/live-search-routing.ts +30 -0
- package/src/components/portable-text-text-align.ts +15 -0
- package/src/ui.ts +2 -0
- package/dist/entity-aliases-Egjmqc7I.mjs +0 -51
- package/dist/import-CvHY6sf5.mjs +0 -684
- package/dist/runner-B-tbV751.mjs +0 -2741
- package/dist/schema-B6zyB-Xn.mjs +0 -8
- package/dist/taxonomies-CNOGeC-3.mjs +0 -327
- package/dist/types-eu5UDgHN.d.mts +0 -652
- package/dist/version-xlzMSXF2.mjs +0 -6
- /package/dist/{adapters-OQCImOL7.d.mts → adapters-NVSs4UH7.d.mts} +0 -0
- /package/dist/{api-tokens-CYUoFGo7.mjs → api-tokens-2r7OWi_I.mjs} +0 -0
- /package/dist/{base64-C1Q9yr0B.mjs → base64-F8-DUraK.mjs} +0 -0
- /package/dist/{challenge-store-CRSz-Xim.mjs → challenge-store-Bjlepa2E.mjs} +0 -0
- /package/dist/{components--pg6zBvu.mjs → components-Bclf98Be.mjs} +0 -0
- /package/dist/{context-types-DQh_XKkn.mjs → context-types-DqTo6C79.mjs} +0 -0
- /package/dist/{default-B6iaLvP7.mjs → default--K40jqm0.mjs} +0 -0
- /package/dist/{email-console-BOVqIYpB.mjs → email-console-ChgKFO8-.mjs} +0 -0
- /package/dist/{escape-BDgouFFY.mjs → escape-Bd4P_xKg.mjs} +0 -0
- /package/dist/{experimental-workflows-Dh4DUoD_.mjs → experimental-workflows-BMsUnIAw.mjs} +0 -0
- /package/dist/{mime-8pAB8-77.mjs → mime-CA7Lgovc.mjs} +0 -0
- /package/dist/{mode-CpyEz8pe.mjs → mode-C4CIyjtR.mjs} +0 -0
- /package/dist/{oauth-state-store-qncPB_IU.mjs → oauth-state-store-CNoXafF7.mjs} +0 -0
- /package/dist/{oauth-user-lookup-BFjWF8wK.mjs → oauth-user-lookup-Ygw4bNyt.mjs} +0 -0
- /package/dist/{options-BvJoSSJR.mjs → options-DQPTH5n0.mjs} +0 -0
- /package/dist/{passkey-config-DzHQK90v.mjs → passkey-config-BUwu8BA3.mjs} +0 -0
- /package/dist/{patterns-r-0n824Y.mjs → patterns-BhAvqO0S.mjs} +0 -0
- /package/dist/{provider-loader-DzUxejk7.mjs → provider-loader-D0w9dqjm.mjs} +0 -0
- /package/dist/{redirect-BjIBtDWY.mjs → redirect-wphRBv4y.mjs} +0 -0
- /package/dist/{request-cache-CYMbwZc6.mjs → request-cache-D9GQBxMP.mjs} +0 -0
- /package/dist/{resolve-DTuDyYKs.mjs → resolve-Cb6Dbz-C.mjs} +0 -0
- /package/dist/{setup-nonce-pyGfc9hV.mjs → setup-nonce-CoyjILvW.mjs} +0 -0
- /package/dist/{sidecar-client-mJxwayYN.mjs → sidecar-client-C3mqXg4C.mjs} +0 -0
- /package/dist/{slugify-BzGxlOFx.mjs → slugify-KSsVD_D3.mjs} +0 -0
- /package/dist/{ssrf-DiKqWd3N.mjs → ssrf-DWe8jwAp.mjs} +0 -0
- /package/dist/{transaction-CShMWIej.mjs → transaction-C2EOMTJA.mjs} +0 -0
- /package/dist/{transport-BnsNxh2l.d.mts → transport-CcJu3Co8.d.mts} +0 -0
- /package/dist/{trusted-proxy-BgIIcbsN.mjs → trusted-proxy-DTXQ2IMv.mjs} +0 -0
- /package/dist/{types-BO8TGjS8.mjs → types-B3t5cxhU.mjs} +0 -0
- /package/dist/{types-nP9vGUFq.d.mts → types-C46EvNdc.d.mts} +0 -0
- /package/dist/{types-Cg276-3z.mjs → types-C5CVgwsV.mjs} +0 -0
- /package/dist/{types-DZpT-PnG.d.mts → types-POH8PQZh.d.mts} +0 -0
- /package/dist/{types-Dryvv7LH.d.mts → types-Rc3gQqqw.d.mts} +0 -0
- /package/dist/{url-w7Mp5qAD.mjs → url-CpyFDFpO.mjs} +0 -0
package/dist/cli/index.mjs
CHANGED
|
@@ -1,51 +1,510 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as __exportAll, i as runMigrations, n as getMigrationStatus, t as getMigrationFingerprint } from "../runner-
|
|
2
|
+
import { a as __exportAll, i as runMigrations, n as getMigrationStatus, t as getMigrationFingerprint } from "../runner-DB7CRfOF.mjs";
|
|
3
3
|
import { t as createDatabase } from "../connection-CiJWiWya.mjs";
|
|
4
4
|
import { t as validateIdentifier } from "../validate-VPnKoIzW.mjs";
|
|
5
5
|
import { l as listTablesLike } from "../dialect-helpers-fk43GbDb.mjs";
|
|
6
6
|
import { n as getI18nConfig, r as isI18nEnabled } from "../config-CVssduLe.mjs";
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import "../
|
|
11
|
-
import {
|
|
12
|
-
import { t as
|
|
13
|
-
import { t as
|
|
14
|
-
import "../
|
|
15
|
-
import "../
|
|
16
|
-
import "../
|
|
17
|
-
import
|
|
18
|
-
import "../
|
|
19
|
-
import
|
|
20
|
-
import {
|
|
21
|
-
import "../
|
|
22
|
-
import
|
|
23
|
-
import {
|
|
24
|
-
import "../
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import
|
|
7
|
+
import { i as encodeBase64url } from "../base64-F8-DUraK.mjs";
|
|
8
|
+
import "../types-Dq1qT19x.mjs";
|
|
9
|
+
import { o as isMissingTableError } from "../loader-CjWqjweO.mjs";
|
|
10
|
+
import "../object-cache-D51qOseS.mjs";
|
|
11
|
+
import { n as slugify } from "../slugify-KSsVD_D3.mjs";
|
|
12
|
+
import { t as ContentRepository } from "../content-Bh4BDjv8.mjs";
|
|
13
|
+
import { t as MediaRepository } from "../media-DMj0grzC.mjs";
|
|
14
|
+
import { t as TaxonomyRepository } from "../taxonomy-Dvsw6BOs.mjs";
|
|
15
|
+
import { t as OptionsRepository } from "../options-DQPTH5n0.mjs";
|
|
16
|
+
import "../redirect-BAjt4Z6b.mjs";
|
|
17
|
+
import "../request-cache-D9GQBxMP.mjs";
|
|
18
|
+
import "../byline-registry-87nNnYl4.mjs";
|
|
19
|
+
import "../field-defs-cache-DRNdhBJv.mjs";
|
|
20
|
+
import { t as BylineRepository } from "../byline-e51-fWRs.mjs";
|
|
21
|
+
import "../content-refresh-CgutSUfO.mjs";
|
|
22
|
+
import "../fts-manager-Crzypjqu.mjs";
|
|
23
|
+
import { n as SchemaRegistry } from "../registry-CtyVedcN.mjs";
|
|
24
|
+
import "../settings-BbqgHWpU.mjs";
|
|
25
|
+
import { n as VERSION } from "../version-BTvfRGc6.mjs";
|
|
26
|
+
import { a as reconcileManifestAccess, i as pluginManifestSchema } from "../manifest-schema-BxVPYMJR.mjs";
|
|
27
|
+
import "../ssrf-DWe8jwAp.mjs";
|
|
28
|
+
import { n as fingerprintKey, r as generateEncryptionKey, t as DinewaySecretsError } from "../secrets-CDZo2k8L.mjs";
|
|
29
|
+
import { t as validateSeed } from "../validate-DNs0pTSc.mjs";
|
|
30
|
+
import { t as applySeed } from "../apply-bZswmZPm.mjs";
|
|
28
31
|
import { createHeaderAwareFetch, customHeadersInterceptor, isRedirectResponse, resolveCustomHeaders } from "../client/external-auth-headers.mjs";
|
|
29
|
-
import {
|
|
32
|
+
import { t as convertDataForRead } from "../portable-text-dA_UlJwW.mjs";
|
|
30
33
|
import { DinewayClient } from "../client/index.mjs";
|
|
31
34
|
import { LocalStorage } from "../storage/local.mjs";
|
|
32
|
-
import {
|
|
35
|
+
import { r as fileExists$11, t as bundleCommand } from "../bundle-7C_DpkEq.mjs";
|
|
36
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
33
37
|
import { sql } from "kysely";
|
|
34
|
-
import { imageSize } from "image-size";
|
|
35
38
|
import { createGzipDecoder, unpackTar } from "modern-tar";
|
|
36
|
-
import { createReadStream,
|
|
37
|
-
import { basename, dirname,
|
|
38
|
-
import { pathToFileURL } from "node:url";
|
|
39
|
+
import { createReadStream, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
40
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
39
41
|
import { defineCommand, runCommand, runMain } from "citty";
|
|
42
|
+
import { createInterface } from "node:readline/promises";
|
|
40
43
|
import consola, { consola as consola$1 } from "consola";
|
|
41
44
|
import pc from "picocolors";
|
|
45
|
+
import { ForgewayClient } from "@forgeway/sdk";
|
|
46
|
+
import { access, chmod, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
42
47
|
import { homedir } from "node:os";
|
|
43
|
-
import { access, chmod, copyFile, mkdir, readFile, readdir, rm, stat, symlink, writeFile } from "node:fs/promises";
|
|
44
|
-
import { createInterface } from "node:readline/promises";
|
|
45
48
|
import { spawn } from "node:child_process";
|
|
46
|
-
import { pipeline } from "node:stream/promises";
|
|
47
|
-
import { packTar } from "modern-tar/fs";
|
|
48
49
|
|
|
50
|
+
//#region src/cli/commands/deploy/utils/forgeway-secrets.ts
|
|
51
|
+
const DINEWAY_USER_CONFIG_DIR_NAME = "dineway";
|
|
52
|
+
const TRAILING_SLASH_PATTERN$3 = /\/$/;
|
|
53
|
+
function getDinewayUserConfigDir() {
|
|
54
|
+
return join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), DINEWAY_USER_CONFIG_DIR_NAME);
|
|
55
|
+
}
|
|
56
|
+
function getForgewayAccountSecretPath() {
|
|
57
|
+
return join(getDinewayUserConfigDir(), "forgeway.json");
|
|
58
|
+
}
|
|
59
|
+
function normalizePlatformApiUrl$1(platformApiUrl) {
|
|
60
|
+
return platformApiUrl.replace(TRAILING_SLASH_PATTERN$3, "");
|
|
61
|
+
}
|
|
62
|
+
function normalizeEmail$2(email) {
|
|
63
|
+
return email.trim().toLowerCase();
|
|
64
|
+
}
|
|
65
|
+
function accountKey(platformApiUrl, email) {
|
|
66
|
+
return `${normalizePlatformApiUrl$1(platformApiUrl)}#${normalizeEmail$2(email)}`;
|
|
67
|
+
}
|
|
68
|
+
function normalizeLookup(lookup) {
|
|
69
|
+
if (typeof lookup === "string") return { cwd: lookup };
|
|
70
|
+
return lookup ?? {};
|
|
71
|
+
}
|
|
72
|
+
async function readAccountStore() {
|
|
73
|
+
try {
|
|
74
|
+
const raw = await readFile(getForgewayAccountSecretPath(), "utf-8");
|
|
75
|
+
return JSON.parse(raw);
|
|
76
|
+
} catch {
|
|
77
|
+
return { version: 1 };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function writeAccountStore(store) {
|
|
81
|
+
await mkdir(getDinewayUserConfigDir(), {
|
|
82
|
+
recursive: true,
|
|
83
|
+
mode: 448
|
|
84
|
+
});
|
|
85
|
+
const file = getForgewayAccountSecretPath();
|
|
86
|
+
await writeFile(file, JSON.stringify({
|
|
87
|
+
...store,
|
|
88
|
+
version: 1
|
|
89
|
+
}, null, " ") + "\n", {
|
|
90
|
+
encoding: "utf-8",
|
|
91
|
+
mode: 384
|
|
92
|
+
});
|
|
93
|
+
await chmod(file, 384);
|
|
94
|
+
}
|
|
95
|
+
function accountMatches(credentials, lookup) {
|
|
96
|
+
if (lookup.platformApiUrl && normalizePlatformApiUrl$1(credentials.platformApiUrl) !== normalizePlatformApiUrl$1(lookup.platformApiUrl)) return false;
|
|
97
|
+
if (lookup.email && normalizeEmail$2(credentials.user?.email ?? "") !== normalizeEmail$2(lookup.email)) return false;
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
async function readForgewayCredentials(lookup) {
|
|
101
|
+
const normalizedLookup = normalizeLookup(lookup);
|
|
102
|
+
const store = await readAccountStore();
|
|
103
|
+
const accounts = Object.values(store.accounts ?? {}).filter((credentials) => accountMatches(credentials, normalizedLookup));
|
|
104
|
+
if (normalizedLookup.platformApiUrl && normalizedLookup.email) return store.accounts?.[accountKey(normalizedLookup.platformApiUrl, normalizedLookup.email)] ?? null;
|
|
105
|
+
if (accounts.length === 1) return accounts[0] ?? null;
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
async function writeForgewayCredentials(credentials, lookup) {
|
|
109
|
+
const normalizedLookup = normalizeLookup(lookup);
|
|
110
|
+
const platformApiUrl = normalizePlatformApiUrl$1(normalizedLookup.platformApiUrl ?? credentials.platformApiUrl);
|
|
111
|
+
const email = normalizeEmail$2(normalizedLookup.email ?? credentials.user?.email ?? "");
|
|
112
|
+
if (!email) throw new Error("Forgeway credentials require a user email before they can be stored.");
|
|
113
|
+
const key = accountKey(platformApiUrl, email);
|
|
114
|
+
const store = await readAccountStore();
|
|
115
|
+
store.accounts = {
|
|
116
|
+
...store.accounts,
|
|
117
|
+
[key]: {
|
|
118
|
+
...credentials,
|
|
119
|
+
platformApiUrl
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
store.lastAccountKey = key;
|
|
123
|
+
await writeAccountStore(store);
|
|
124
|
+
}
|
|
125
|
+
async function listForgewayCredentials(platformApiUrl) {
|
|
126
|
+
const lookup = platformApiUrl ? { platformApiUrl } : {};
|
|
127
|
+
const store = await readAccountStore();
|
|
128
|
+
return Object.values(store.accounts ?? {}).filter((credentials) => accountMatches(credentials, lookup));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/cli/forgeway-account.ts
|
|
133
|
+
const TRAILING_SLASH_PATTERN$2 = /\/$/;
|
|
134
|
+
const SHADOW_EMAIL_PATTERN$2 = /^shadow_[a-f0-9]{16}@dineway\.ai$/i;
|
|
135
|
+
const DEFAULT_FORGEWAY_API_BASE_URL = "https://api.dineway.ai";
|
|
136
|
+
const DEFAULT_DINEWAY_WEB_URL = "https://dineway.ai";
|
|
137
|
+
function normalizeForgewayApiUrl(value) {
|
|
138
|
+
return (value || DEFAULT_FORGEWAY_API_BASE_URL).replace(TRAILING_SLASH_PATTERN$2, "");
|
|
139
|
+
}
|
|
140
|
+
function normalizeDinewayWebUrl(value) {
|
|
141
|
+
return (value || DEFAULT_DINEWAY_WEB_URL).replace(TRAILING_SLASH_PATTERN$2, "");
|
|
142
|
+
}
|
|
143
|
+
function normalizeForgewayEmail(value) {
|
|
144
|
+
return value?.trim().toLowerCase() ?? "";
|
|
145
|
+
}
|
|
146
|
+
function isShadowForgewayEmail(email) {
|
|
147
|
+
return Boolean(email && SHADOW_EMAIL_PATTERN$2.test(email));
|
|
148
|
+
}
|
|
149
|
+
function createForgewayAccountClient(platformApiUrl, accessToken) {
|
|
150
|
+
return new ForgewayClient({
|
|
151
|
+
baseUrl: normalizeForgewayApiUrl(platformApiUrl),
|
|
152
|
+
accessToken,
|
|
153
|
+
isServerMode: true,
|
|
154
|
+
auth: { detectOAuthCallback: false }
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function normalizeForgewayUser$2(user) {
|
|
158
|
+
if (!user?.id || !user.email) throw new Error("Forgeway session response did not include a user email.");
|
|
159
|
+
return {
|
|
160
|
+
id: user.id,
|
|
161
|
+
name: user.profile?.name ?? user.name ?? user.email,
|
|
162
|
+
email: user.email,
|
|
163
|
+
avatarUrl: user.profile?.avatar_url ?? user.avatarUrl ?? user.avatar_url ?? null,
|
|
164
|
+
emailVerified: user.emailVerified ?? user.email_verified ?? false
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function assertFormalForgewayUser(user) {
|
|
168
|
+
if (!user || user.emailVerified !== true || isShadowForgewayEmail(user.email)) throw new Error("Dineway account commands require a verified Forgeway account.");
|
|
169
|
+
return user;
|
|
170
|
+
}
|
|
171
|
+
async function readStoredForgewayAccount(lookup = {}) {
|
|
172
|
+
const platformApiUrl = normalizeForgewayApiUrl(lookup.platformApiUrl);
|
|
173
|
+
const email = normalizeForgewayEmail(lookup.email);
|
|
174
|
+
if (email) return readForgewayCredentials({
|
|
175
|
+
platformApiUrl,
|
|
176
|
+
email
|
|
177
|
+
});
|
|
178
|
+
const stored = await readForgewayCredentials({ platformApiUrl });
|
|
179
|
+
if (stored) return stored;
|
|
180
|
+
const accounts = (await listForgewayCredentials(platformApiUrl)).filter((credentials) => {
|
|
181
|
+
try {
|
|
182
|
+
assertFormalForgewayUser(credentials.user);
|
|
183
|
+
return true;
|
|
184
|
+
} catch {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
return accounts.length === 1 ? accounts[0] ?? null : null;
|
|
189
|
+
}
|
|
190
|
+
async function refreshForgewayAccountCredentials(credentials) {
|
|
191
|
+
const user = assertFormalForgewayUser(credentials.user);
|
|
192
|
+
if (!credentials.refreshToken) throw new Error("Stored Forgeway account is missing a refresh token. Run `dineway account login`.");
|
|
193
|
+
const { data, error } = await createForgewayAccountClient(credentials.platformApiUrl).auth.refreshSession({ refreshToken: credentials.refreshToken });
|
|
194
|
+
if (error || !data?.accessToken) throw new Error(error?.message || "Failed to refresh Forgeway account. Run `dineway account login`.");
|
|
195
|
+
const refreshedUser = normalizeForgewayUser$2(data.user);
|
|
196
|
+
if (normalizeForgewayEmail(refreshedUser.email) !== normalizeForgewayEmail(user.email)) throw new Error("Forgeway refresh returned credentials for a different account.");
|
|
197
|
+
const updated = {
|
|
198
|
+
...credentials,
|
|
199
|
+
platformApiUrl: normalizeForgewayApiUrl(credentials.platformApiUrl),
|
|
200
|
+
accessToken: data.accessToken,
|
|
201
|
+
refreshToken: data.refreshToken ?? credentials.refreshToken,
|
|
202
|
+
user: {
|
|
203
|
+
...refreshedUser,
|
|
204
|
+
emailVerified: true
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
await writeForgewayCredentials(updated, {
|
|
208
|
+
platformApiUrl: updated.platformApiUrl,
|
|
209
|
+
email: updated.user?.email
|
|
210
|
+
});
|
|
211
|
+
return updated;
|
|
212
|
+
}
|
|
213
|
+
async function sendForgewayAccountLoginLink(input) {
|
|
214
|
+
const platformApiUrl = normalizeForgewayApiUrl(input.platformApiUrl);
|
|
215
|
+
const webUrl = normalizeDinewayWebUrl(input.webUrl);
|
|
216
|
+
const redirectTo = new URL("/cli-auth", webUrl).toString();
|
|
217
|
+
const { error } = await createForgewayAccountClient(platformApiUrl).auth.sendPasswordlessSignInEmail({
|
|
218
|
+
email: input.email,
|
|
219
|
+
redirectTo
|
|
220
|
+
});
|
|
221
|
+
if (error) throw new Error(error.message || "Failed to send Forgeway account sign-in link.");
|
|
222
|
+
}
|
|
223
|
+
async function completeForgewayAccountLogin(input) {
|
|
224
|
+
const platformApiUrl = normalizeForgewayApiUrl(input.platformApiUrl);
|
|
225
|
+
const { data, error } = await createForgewayAccountClient(platformApiUrl).auth.signInWithPasswordlessToken({ token: input.token });
|
|
226
|
+
if (error || !data?.accessToken || !data.refreshToken) throw new Error(error?.message || "Forgeway sign-in token is invalid or expired.");
|
|
227
|
+
const user = normalizeForgewayUser$2(data.user);
|
|
228
|
+
if (input.email && normalizeForgewayEmail(user.email) !== normalizeForgewayEmail(input.email)) throw new Error("Forgeway sign-in returned credentials for a different email.");
|
|
229
|
+
const credentials = {
|
|
230
|
+
platformApiUrl,
|
|
231
|
+
accessToken: data.accessToken,
|
|
232
|
+
refreshToken: data.refreshToken,
|
|
233
|
+
user: {
|
|
234
|
+
...user,
|
|
235
|
+
emailVerified: true
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
await writeForgewayCredentials(credentials, {
|
|
239
|
+
platformApiUrl,
|
|
240
|
+
email: credentials.user?.email
|
|
241
|
+
});
|
|
242
|
+
return credentials;
|
|
243
|
+
}
|
|
244
|
+
async function getForgewayDinewayAccount(credentials) {
|
|
245
|
+
const refreshed = await refreshForgewayAccountCredentials(credentials);
|
|
246
|
+
const { data, error } = await createForgewayAccountClient(refreshed.platformApiUrl, refreshed.accessToken).dineway.getAccount();
|
|
247
|
+
if (error || !data) throw new Error(error?.message || "Failed to fetch Dineway account status from Forgeway.");
|
|
248
|
+
return {
|
|
249
|
+
credentials: refreshed,
|
|
250
|
+
account: data
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
//#endregion
|
|
255
|
+
//#region src/cli/output.ts
|
|
256
|
+
/**
|
|
257
|
+
* Redirect consola output to stderr so it doesn't pollute JSON on stdout.
|
|
258
|
+
*
|
|
259
|
+
* Call this early in any command that uses `output()` with `--json`.
|
|
260
|
+
* Safe to call multiple times — only applies the redirect once.
|
|
261
|
+
*/
|
|
262
|
+
function configureOutputMode(args) {
|
|
263
|
+
if (args.json || !process.stdout.isTTY) {
|
|
264
|
+
consola$1.options.stdout = process.stderr;
|
|
265
|
+
consola$1.options.stderr = process.stderr;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Output data as JSON or pretty-printed.
|
|
270
|
+
*
|
|
271
|
+
* If stdout is not a TTY or --json is set, outputs JSON.
|
|
272
|
+
* Otherwise, outputs a formatted representation.
|
|
273
|
+
*/
|
|
274
|
+
function output(data, args) {
|
|
275
|
+
if (args.json || !process.stdout.isTTY) process.stdout.write(JSON.stringify(data, null, 2) + "\n");
|
|
276
|
+
else prettyPrint(data);
|
|
277
|
+
}
|
|
278
|
+
function prettyPrint(data, indent = 0) {
|
|
279
|
+
if (data === null || data === void 0) {
|
|
280
|
+
consola$1.log("(empty)");
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (Array.isArray(data)) {
|
|
284
|
+
if (data.length === 0) {
|
|
285
|
+
consola$1.log("(no items)");
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
for (const item of data) {
|
|
289
|
+
prettyPrint(item, indent);
|
|
290
|
+
if (indent === 0) consola$1.log("---");
|
|
291
|
+
}
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
if (typeof data === "object") {
|
|
295
|
+
const obj = Object(data);
|
|
296
|
+
if ("items" in obj && Array.isArray(obj.items)) {
|
|
297
|
+
prettyPrint(obj.items, indent);
|
|
298
|
+
if (typeof obj.nextCursor === "string") consola$1.log(`\nNext cursor: ${obj.nextCursor}`);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const prefix = " ".repeat(indent);
|
|
302
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
303
|
+
if (value === null || value === void 0) continue;
|
|
304
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
305
|
+
consola$1.log(`${prefix}${key}:`);
|
|
306
|
+
prettyPrint(value, indent + 1);
|
|
307
|
+
} else if (Array.isArray(value)) consola$1.log(`${prefix}${key}: [${value.length} items]`);
|
|
308
|
+
else {
|
|
309
|
+
const str = typeof value === "string" ? value : JSON.stringify(value);
|
|
310
|
+
const display = str.length > 80 ? str.slice(0, 77) + "..." : str;
|
|
311
|
+
consola$1.log(`${prefix}${key}: ${display}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
consola$1.log(typeof data === "string" ? data : JSON.stringify(data));
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
//#endregion
|
|
320
|
+
//#region src/cli/commands/account.ts
|
|
321
|
+
async function promptLine(question) {
|
|
322
|
+
const readline = createInterface({
|
|
323
|
+
input: process.stdin,
|
|
324
|
+
output: process.stdout
|
|
325
|
+
});
|
|
326
|
+
try {
|
|
327
|
+
return (await readline.question(question)).trim();
|
|
328
|
+
} finally {
|
|
329
|
+
readline.close();
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
function resolveApiUrl(args) {
|
|
333
|
+
return normalizeForgewayApiUrl(args["api-url"] ?? process.env.DINEWAY_API_BASE_URL ?? process.env.FORGEWAY_API_URL);
|
|
334
|
+
}
|
|
335
|
+
function outputAccountJson(value) {
|
|
336
|
+
console.log(JSON.stringify(value, null, " "));
|
|
337
|
+
}
|
|
338
|
+
const accountLoginCommand = defineCommand({
|
|
339
|
+
meta: {
|
|
340
|
+
name: "login",
|
|
341
|
+
description: "Log in to the Dineway Web / Forgeway account used for paid services"
|
|
342
|
+
},
|
|
343
|
+
args: {
|
|
344
|
+
email: {
|
|
345
|
+
type: "string",
|
|
346
|
+
alias: "e",
|
|
347
|
+
description: "Forgeway account email"
|
|
348
|
+
},
|
|
349
|
+
token: {
|
|
350
|
+
type: "string",
|
|
351
|
+
description: "Passwordless sign-in token from the Dineway CLI auth page"
|
|
352
|
+
},
|
|
353
|
+
"api-url": {
|
|
354
|
+
type: "string",
|
|
355
|
+
description: "Forgeway API URL",
|
|
356
|
+
default: DEFAULT_FORGEWAY_API_BASE_URL
|
|
357
|
+
},
|
|
358
|
+
"web-url": {
|
|
359
|
+
type: "string",
|
|
360
|
+
description: "Dineway Web URL used for the email link",
|
|
361
|
+
default: DEFAULT_DINEWAY_WEB_URL
|
|
362
|
+
},
|
|
363
|
+
"send-link": {
|
|
364
|
+
type: "boolean",
|
|
365
|
+
description: "Send the email link and exit without prompting for the token"
|
|
366
|
+
},
|
|
367
|
+
json: {
|
|
368
|
+
type: "boolean",
|
|
369
|
+
description: "Output as JSON"
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
async run({ args }) {
|
|
373
|
+
configureOutputMode(args);
|
|
374
|
+
const platformApiUrl = resolveApiUrl(args);
|
|
375
|
+
const webUrl = normalizeDinewayWebUrl(args["web-url"]);
|
|
376
|
+
const email = normalizeForgewayEmail(args.email) || normalizeForgewayEmail(await promptLine("Dineway account email: "));
|
|
377
|
+
if (!email) {
|
|
378
|
+
consola$1.error("Email is required.");
|
|
379
|
+
process.exit(2);
|
|
380
|
+
}
|
|
381
|
+
try {
|
|
382
|
+
const existing = await readStoredForgewayAccount({
|
|
383
|
+
platformApiUrl,
|
|
384
|
+
email
|
|
385
|
+
});
|
|
386
|
+
if (existing && !args.token && !args["send-link"]) {
|
|
387
|
+
const { account } = await getForgewayDinewayAccount(existing);
|
|
388
|
+
if (args.json) outputAccountJson({
|
|
389
|
+
loggedIn: true,
|
|
390
|
+
platformApiUrl,
|
|
391
|
+
account
|
|
392
|
+
});
|
|
393
|
+
else {
|
|
394
|
+
consola$1.success(`Already logged in as ${pc.bold(account.account.email ?? email)}`);
|
|
395
|
+
consola$1.info(`Plan: ${pc.cyan(account.plan.name)}`);
|
|
396
|
+
}
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
if (!args.token) {
|
|
400
|
+
await sendForgewayAccountLoginLink({
|
|
401
|
+
email,
|
|
402
|
+
platformApiUrl,
|
|
403
|
+
webUrl
|
|
404
|
+
});
|
|
405
|
+
if (args.json) outputAccountJson({
|
|
406
|
+
sent: true,
|
|
407
|
+
email,
|
|
408
|
+
platformApiUrl,
|
|
409
|
+
cliAuthUrl: new URL("/cli-auth", webUrl).toString()
|
|
410
|
+
});
|
|
411
|
+
else {
|
|
412
|
+
consola$1.success(`Sent Dineway account sign-in link to ${pc.bold(email)}`);
|
|
413
|
+
consola$1.info("Open the email link, copy the CLI token, then paste it here.");
|
|
414
|
+
}
|
|
415
|
+
if (args["send-link"]) return;
|
|
416
|
+
}
|
|
417
|
+
const { account } = await getForgewayDinewayAccount(await completeForgewayAccountLogin({
|
|
418
|
+
token: typeof args.token === "string" && args.token.trim() ? args.token.trim() : await promptLine("CLI token: "),
|
|
419
|
+
platformApiUrl,
|
|
420
|
+
email
|
|
421
|
+
}));
|
|
422
|
+
if (args.json) outputAccountJson({
|
|
423
|
+
loggedIn: true,
|
|
424
|
+
platformApiUrl,
|
|
425
|
+
account
|
|
426
|
+
});
|
|
427
|
+
else {
|
|
428
|
+
consola$1.success(`Logged in as ${pc.bold(account.account.email ?? email)}`);
|
|
429
|
+
consola$1.info(`Plan: ${pc.cyan(account.plan.name)}`);
|
|
430
|
+
consola$1.info(`Credits: ${account.credits.availableCredits.toLocaleString()}`);
|
|
431
|
+
consola$1.info(`Hosted sites: ${account.sites.usedHostedSites.toLocaleString()} / ${account.sites.hostedSiteLimitLabel}`);
|
|
432
|
+
}
|
|
433
|
+
} catch (error) {
|
|
434
|
+
consola$1.error(error instanceof Error ? error.message : "Dineway account login failed");
|
|
435
|
+
process.exit(2);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
const accountStatusCommand = defineCommand({
|
|
440
|
+
meta: {
|
|
441
|
+
name: "status",
|
|
442
|
+
description: "Show Dineway Web / Forgeway account plan, credits, and hosted site slots"
|
|
443
|
+
},
|
|
444
|
+
args: {
|
|
445
|
+
email: {
|
|
446
|
+
type: "string",
|
|
447
|
+
alias: "e",
|
|
448
|
+
description: "Forgeway account email"
|
|
449
|
+
},
|
|
450
|
+
"api-url": {
|
|
451
|
+
type: "string",
|
|
452
|
+
description: "Forgeway API URL",
|
|
453
|
+
default: DEFAULT_FORGEWAY_API_BASE_URL
|
|
454
|
+
},
|
|
455
|
+
json: {
|
|
456
|
+
type: "boolean",
|
|
457
|
+
description: "Output as JSON"
|
|
458
|
+
}
|
|
459
|
+
},
|
|
460
|
+
async run({ args }) {
|
|
461
|
+
configureOutputMode(args);
|
|
462
|
+
const platformApiUrl = resolveApiUrl(args);
|
|
463
|
+
try {
|
|
464
|
+
const credentials = await readStoredForgewayAccount({
|
|
465
|
+
platformApiUrl,
|
|
466
|
+
email: normalizeForgewayEmail(args.email)
|
|
467
|
+
});
|
|
468
|
+
if (!credentials) {
|
|
469
|
+
if (args.json) outputAccountJson({
|
|
470
|
+
loggedIn: false,
|
|
471
|
+
platformApiUrl
|
|
472
|
+
});
|
|
473
|
+
else consola$1.error("Not logged in. Run: dineway account login");
|
|
474
|
+
process.exit(2);
|
|
475
|
+
}
|
|
476
|
+
const { account } = await getForgewayDinewayAccount(credentials);
|
|
477
|
+
if (args.json) {
|
|
478
|
+
outputAccountJson({
|
|
479
|
+
loggedIn: true,
|
|
480
|
+
platformApiUrl,
|
|
481
|
+
account
|
|
482
|
+
});
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
consola$1.info(`Email: ${pc.bold(account.account.email ?? credentials.user?.email ?? "unknown")}`);
|
|
486
|
+
consola$1.info(`Plan: ${pc.cyan(account.plan.name)}`);
|
|
487
|
+
consola$1.info(`Credits available: ${account.credits.availableCredits.toLocaleString()}`);
|
|
488
|
+
consola$1.info(`Hosted sites: ${account.sites.usedHostedSites.toLocaleString()} / ${account.sites.hostedSiteLimitLabel}`);
|
|
489
|
+
if (account.billing.subscription?.status) consola$1.info(`Billing: ${account.billing.subscription.status}`);
|
|
490
|
+
} catch (error) {
|
|
491
|
+
consola$1.error(error instanceof Error ? error.message : "Dineway account status failed");
|
|
492
|
+
process.exit(2);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
const accountCommand = defineCommand({
|
|
497
|
+
meta: {
|
|
498
|
+
name: "account",
|
|
499
|
+
description: "Dineway Web account login, status, and billing helpers"
|
|
500
|
+
},
|
|
501
|
+
subCommands: {
|
|
502
|
+
login: accountLoginCommand,
|
|
503
|
+
status: accountStatusCommand
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
//#endregion
|
|
49
508
|
//#region src/cli/credentials.ts
|
|
50
509
|
/**
|
|
51
510
|
* Credential storage for CLI auth tokens.
|
|
@@ -99,7 +558,7 @@ function findProjectRoot(from) {
|
|
|
99
558
|
}
|
|
100
559
|
return null;
|
|
101
560
|
}
|
|
102
|
-
function readStore
|
|
561
|
+
function readStore() {
|
|
103
562
|
const path = getCredentialPath();
|
|
104
563
|
try {
|
|
105
564
|
if (existsSync(path)) {
|
|
@@ -109,7 +568,7 @@ function readStore$1() {
|
|
|
109
568
|
} catch {}
|
|
110
569
|
return {};
|
|
111
570
|
}
|
|
112
|
-
function writeStore
|
|
571
|
+
function writeStore(store) {
|
|
113
572
|
mkdirSync(getConfigDir(), {
|
|
114
573
|
recursive: true,
|
|
115
574
|
mode: 448
|
|
@@ -124,7 +583,7 @@ function writeStore$1(store) {
|
|
|
124
583
|
*/
|
|
125
584
|
function getCredentials(baseUrl) {
|
|
126
585
|
const key = resolveCredentialKey(baseUrl);
|
|
127
|
-
const cred = readStore
|
|
586
|
+
const cred = readStore()[key];
|
|
128
587
|
if (!cred || !("accessToken" in cred)) return null;
|
|
129
588
|
return cred;
|
|
130
589
|
}
|
|
@@ -133,19 +592,19 @@ function getCredentials(baseUrl) {
|
|
|
133
592
|
*/
|
|
134
593
|
function saveCredentials(baseUrl, cred) {
|
|
135
594
|
const key = resolveCredentialKey(baseUrl);
|
|
136
|
-
const store = readStore
|
|
595
|
+
const store = readStore();
|
|
137
596
|
store[key] = cred;
|
|
138
|
-
writeStore
|
|
597
|
+
writeStore(store);
|
|
139
598
|
}
|
|
140
599
|
/**
|
|
141
600
|
* Remove credentials for a URL.
|
|
142
601
|
*/
|
|
143
602
|
function removeCredentials(baseUrl) {
|
|
144
603
|
const key = resolveCredentialKey(baseUrl);
|
|
145
|
-
const store = readStore
|
|
604
|
+
const store = readStore();
|
|
146
605
|
if (key in store) {
|
|
147
606
|
delete store[key];
|
|
148
|
-
writeStore
|
|
607
|
+
writeStore(store);
|
|
149
608
|
return true;
|
|
150
609
|
}
|
|
151
610
|
return false;
|
|
@@ -162,7 +621,7 @@ function marketplaceKey(registryUrl) {
|
|
|
162
621
|
*/
|
|
163
622
|
function getMarketplaceCredential(registryUrl) {
|
|
164
623
|
const key = marketplaceKey(registryUrl);
|
|
165
|
-
const cred = readStore
|
|
624
|
+
const cred = readStore()[key];
|
|
166
625
|
if (!cred || !("token" in cred)) return null;
|
|
167
626
|
if (new Date(cred.expiresAt) < /* @__PURE__ */ new Date()) return null;
|
|
168
627
|
return cred;
|
|
@@ -172,19 +631,19 @@ function getMarketplaceCredential(registryUrl) {
|
|
|
172
631
|
*/
|
|
173
632
|
function saveMarketplaceCredential(registryUrl, cred) {
|
|
174
633
|
const key = marketplaceKey(registryUrl);
|
|
175
|
-
const store = readStore
|
|
634
|
+
const store = readStore();
|
|
176
635
|
store[key] = cred;
|
|
177
|
-
writeStore
|
|
636
|
+
writeStore(store);
|
|
178
637
|
}
|
|
179
638
|
/**
|
|
180
639
|
* Remove marketplace credential for a registry URL.
|
|
181
640
|
*/
|
|
182
641
|
function removeMarketplaceCredential(registryUrl) {
|
|
183
642
|
const key = marketplaceKey(registryUrl);
|
|
184
|
-
const store = readStore
|
|
643
|
+
const store = readStore();
|
|
185
644
|
if (key in store) {
|
|
186
645
|
delete store[key];
|
|
187
|
-
writeStore
|
|
646
|
+
writeStore(store);
|
|
188
647
|
return true;
|
|
189
648
|
}
|
|
190
649
|
return false;
|
|
@@ -303,125 +762,60 @@ const connectionArgs = {
|
|
|
303
762
|
description: "Auth token"
|
|
304
763
|
},
|
|
305
764
|
header: {
|
|
306
|
-
type: "string",
|
|
307
|
-
alias: "H",
|
|
308
|
-
description: "Custom header \"Name: Value\" (repeatable, or use DINEWAY_HEADERS env)"
|
|
309
|
-
},
|
|
310
|
-
json: {
|
|
311
|
-
type: "boolean",
|
|
312
|
-
description: "Output as JSON"
|
|
313
|
-
}
|
|
314
|
-
};
|
|
315
|
-
/**
|
|
316
|
-
* Create a DinewayClient from CLI args, env vars, and stored credentials.
|
|
317
|
-
*
|
|
318
|
-
* Auth resolution order:
|
|
319
|
-
* 1. --token flag
|
|
320
|
-
* 2. DINEWAY_TOKEN env var
|
|
321
|
-
* 3. Stored credentials (~/.config/dineway/auth.json)
|
|
322
|
-
* 4. Dev bypass (if URL is localhost)
|
|
323
|
-
*
|
|
324
|
-
* Custom headers are merged from (in priority order):
|
|
325
|
-
* 1. Stored credentials (persisted during `dineway login --header`)
|
|
326
|
-
* 2. DINEWAY_HEADERS env var
|
|
327
|
-
* 3. --header CLI flags
|
|
328
|
-
*/
|
|
329
|
-
function createClientFromArgs(args) {
|
|
330
|
-
const baseUrl = resolveDinewayBaseUrl(args.url);
|
|
331
|
-
let token = resolveDinewayToken(args.token);
|
|
332
|
-
const isLocal = baseUrl.includes("localhost") || baseUrl.includes("127.0.0.1");
|
|
333
|
-
const cred = !token ? getCredentials(baseUrl) : null;
|
|
334
|
-
const customHeaders = {
|
|
335
|
-
...cred?.customHeaders,
|
|
336
|
-
...resolveCustomHeaders()
|
|
337
|
-
};
|
|
338
|
-
const extraInterceptors = [];
|
|
339
|
-
if (Object.keys(customHeaders).length > 0) extraInterceptors.push(customHeadersInterceptor(customHeaders));
|
|
340
|
-
if (!token && cred) if (new Date(cred.expiresAt) > /* @__PURE__ */ new Date()) token = cred.accessToken;
|
|
341
|
-
else return new DinewayClient({
|
|
342
|
-
baseUrl,
|
|
343
|
-
token: cred.accessToken,
|
|
344
|
-
refreshToken: cred.refreshToken,
|
|
345
|
-
onTokenRefresh: (newAccessToken, expiresIn) => {
|
|
346
|
-
saveCredentials(baseUrl, {
|
|
347
|
-
...cred,
|
|
348
|
-
accessToken: newAccessToken,
|
|
349
|
-
expiresAt: new Date(Date.now() + expiresIn * 1e3).toISOString()
|
|
350
|
-
});
|
|
351
|
-
},
|
|
352
|
-
interceptors: extraInterceptors
|
|
353
|
-
});
|
|
354
|
-
return new DinewayClient({
|
|
355
|
-
baseUrl,
|
|
356
|
-
token,
|
|
357
|
-
devBypass: !token && isLocal,
|
|
358
|
-
interceptors: extraInterceptors
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
//#endregion
|
|
363
|
-
//#region src/cli/output.ts
|
|
364
|
-
/**
|
|
365
|
-
* Redirect consola output to stderr so it doesn't pollute JSON on stdout.
|
|
366
|
-
*
|
|
367
|
-
* Call this early in any command that uses `output()` with `--json`.
|
|
368
|
-
* Safe to call multiple times — only applies the redirect once.
|
|
369
|
-
*/
|
|
370
|
-
function configureOutputMode(args) {
|
|
371
|
-
if (args.json || !process.stdout.isTTY) {
|
|
372
|
-
consola$1.options.stdout = process.stderr;
|
|
373
|
-
consola$1.options.stderr = process.stderr;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
/**
|
|
377
|
-
* Output data as JSON or pretty-printed.
|
|
378
|
-
*
|
|
379
|
-
* If stdout is not a TTY or --json is set, outputs JSON.
|
|
380
|
-
* Otherwise, outputs a formatted representation.
|
|
381
|
-
*/
|
|
382
|
-
function output(data, args) {
|
|
383
|
-
if (args.json || !process.stdout.isTTY) process.stdout.write(JSON.stringify(data, null, 2) + "\n");
|
|
384
|
-
else prettyPrint(data);
|
|
385
|
-
}
|
|
386
|
-
function prettyPrint(data, indent = 0) {
|
|
387
|
-
if (data === null || data === void 0) {
|
|
388
|
-
consola$1.log("(empty)");
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
391
|
-
if (Array.isArray(data)) {
|
|
392
|
-
if (data.length === 0) {
|
|
393
|
-
consola$1.log("(no items)");
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
for (const item of data) {
|
|
397
|
-
prettyPrint(item, indent);
|
|
398
|
-
if (indent === 0) consola$1.log("---");
|
|
399
|
-
}
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
402
|
-
if (typeof data === "object") {
|
|
403
|
-
const obj = Object(data);
|
|
404
|
-
if ("items" in obj && Array.isArray(obj.items)) {
|
|
405
|
-
prettyPrint(obj.items, indent);
|
|
406
|
-
if (typeof obj.nextCursor === "string") consola$1.log(`\nNext cursor: ${obj.nextCursor}`);
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
const prefix = " ".repeat(indent);
|
|
410
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
411
|
-
if (value === null || value === void 0) continue;
|
|
412
|
-
if (typeof value === "object" && !Array.isArray(value)) {
|
|
413
|
-
consola$1.log(`${prefix}${key}:`);
|
|
414
|
-
prettyPrint(value, indent + 1);
|
|
415
|
-
} else if (Array.isArray(value)) consola$1.log(`${prefix}${key}: [${value.length} items]`);
|
|
416
|
-
else {
|
|
417
|
-
const str = typeof value === "string" ? value : JSON.stringify(value);
|
|
418
|
-
const display = str.length > 80 ? str.slice(0, 77) + "..." : str;
|
|
419
|
-
consola$1.log(`${prefix}${key}: ${display}`);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
return;
|
|
765
|
+
type: "string",
|
|
766
|
+
alias: "H",
|
|
767
|
+
description: "Custom header \"Name: Value\" (repeatable, or use DINEWAY_HEADERS env)"
|
|
768
|
+
},
|
|
769
|
+
json: {
|
|
770
|
+
type: "boolean",
|
|
771
|
+
description: "Output as JSON"
|
|
423
772
|
}
|
|
424
|
-
|
|
773
|
+
};
|
|
774
|
+
/**
|
|
775
|
+
* Create a DinewayClient from CLI args, env vars, and stored credentials.
|
|
776
|
+
*
|
|
777
|
+
* Auth resolution order:
|
|
778
|
+
* 1. --token flag
|
|
779
|
+
* 2. DINEWAY_TOKEN env var
|
|
780
|
+
* 3. Stored credentials (~/.config/dineway/auth.json)
|
|
781
|
+
* 4. Dev bypass (if URL is localhost)
|
|
782
|
+
*
|
|
783
|
+
* Custom headers are merged from (in priority order):
|
|
784
|
+
* 1. Stored credentials (persisted during `dineway login --header`)
|
|
785
|
+
* 2. DINEWAY_HEADERS env var
|
|
786
|
+
* 3. --header CLI flags
|
|
787
|
+
*/
|
|
788
|
+
function createClientFromArgs(args) {
|
|
789
|
+
const baseUrl = resolveDinewayBaseUrl(args.url);
|
|
790
|
+
let token = resolveDinewayToken(args.token);
|
|
791
|
+
const isLocal = baseUrl.includes("localhost") || baseUrl.includes("127.0.0.1");
|
|
792
|
+
const cred = !token ? getCredentials(baseUrl) : null;
|
|
793
|
+
const customHeaders = {
|
|
794
|
+
...cred?.customHeaders,
|
|
795
|
+
...resolveCustomHeaders()
|
|
796
|
+
};
|
|
797
|
+
const extraInterceptors = [];
|
|
798
|
+
if (Object.keys(customHeaders).length > 0) extraInterceptors.push(customHeadersInterceptor(customHeaders));
|
|
799
|
+
if (!token && cred) if (new Date(cred.expiresAt) > /* @__PURE__ */ new Date()) token = cred.accessToken;
|
|
800
|
+
else return new DinewayClient({
|
|
801
|
+
baseUrl,
|
|
802
|
+
token: cred.accessToken,
|
|
803
|
+
refreshToken: cred.refreshToken,
|
|
804
|
+
onTokenRefresh: (newAccessToken, expiresIn) => {
|
|
805
|
+
saveCredentials(baseUrl, {
|
|
806
|
+
...cred,
|
|
807
|
+
accessToken: newAccessToken,
|
|
808
|
+
expiresAt: new Date(Date.now() + expiresIn * 1e3).toISOString()
|
|
809
|
+
});
|
|
810
|
+
},
|
|
811
|
+
interceptors: extraInterceptors
|
|
812
|
+
});
|
|
813
|
+
return new DinewayClient({
|
|
814
|
+
baseUrl,
|
|
815
|
+
token,
|
|
816
|
+
devBypass: !token && isLocal,
|
|
817
|
+
interceptors: extraInterceptors
|
|
818
|
+
});
|
|
425
819
|
}
|
|
426
820
|
|
|
427
821
|
//#endregion
|
|
@@ -952,6 +1346,164 @@ const contentCommand = defineCommand({
|
|
|
952
1346
|
}
|
|
953
1347
|
});
|
|
954
1348
|
|
|
1349
|
+
//#endregion
|
|
1350
|
+
//#region src/cli/paywall.ts
|
|
1351
|
+
const PAYWALL_MESSAGE_PATTERN = /\b(quota|credit|credits|upgrade|plan|billing|subscription|slot)\b/i;
|
|
1352
|
+
function isRecord$1(value) {
|
|
1353
|
+
return typeof value === "object" && value !== null;
|
|
1354
|
+
}
|
|
1355
|
+
function stringField(record, key) {
|
|
1356
|
+
const value = record[key];
|
|
1357
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
1358
|
+
}
|
|
1359
|
+
function getNestedError(value) {
|
|
1360
|
+
if (!isRecord$1(value)) return null;
|
|
1361
|
+
const nested = value.error;
|
|
1362
|
+
return isRecord$1(nested) ? nested : value;
|
|
1363
|
+
}
|
|
1364
|
+
function isHostedSitesResource(resource) {
|
|
1365
|
+
const normalized = resource.toLowerCase();
|
|
1366
|
+
return normalized === "deploy:forgeway" || normalized === "dineway.hosted_sites" || normalized.includes("hosted_site");
|
|
1367
|
+
}
|
|
1368
|
+
function isCreditResource(resource) {
|
|
1369
|
+
const normalized = resource.toLowerCase();
|
|
1370
|
+
return normalized.includes("credit") || normalized.includes("firecrawl") || normalized.includes("dataforseo");
|
|
1371
|
+
}
|
|
1372
|
+
function isQuotaResource(resource) {
|
|
1373
|
+
const normalized = resource.toLowerCase();
|
|
1374
|
+
return normalized.includes("quota") || normalized.includes("place_search") || normalized.includes("place_get") || normalized.includes("/api/places");
|
|
1375
|
+
}
|
|
1376
|
+
function getCliPaywallRefreshStatus(paywall, account) {
|
|
1377
|
+
if (isHostedSitesResource(paywall.resource)) {
|
|
1378
|
+
const remainingHostedSites = account.sites?.remainingHostedSites;
|
|
1379
|
+
if (remainingHostedSites === null || (remainingHostedSites ?? 0) > 0) return {
|
|
1380
|
+
blocked: false,
|
|
1381
|
+
message: "Hosted capacity is now available. Re-run the blocked command."
|
|
1382
|
+
};
|
|
1383
|
+
return {
|
|
1384
|
+
blocked: true,
|
|
1385
|
+
message: "There is still no hosted capacity. Complete checkout in Dineway Web."
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1388
|
+
if (isCreditResource(paywall.resource)) {
|
|
1389
|
+
if ((account.credits?.availableCredits ?? 0) > 0) return {
|
|
1390
|
+
blocked: false,
|
|
1391
|
+
message: "Provider credits are now available. Re-run the blocked command."
|
|
1392
|
+
};
|
|
1393
|
+
return {
|
|
1394
|
+
blocked: true,
|
|
1395
|
+
message: "There are still no provider credits. Complete checkout or top up credits in Dineway Web."
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
if (isQuotaResource(paywall.resource)) return {
|
|
1399
|
+
blocked: false,
|
|
1400
|
+
message: "Account quota status refreshed. Re-run the blocked command when ready."
|
|
1401
|
+
};
|
|
1402
|
+
return {
|
|
1403
|
+
blocked: false,
|
|
1404
|
+
message: "Account status refreshed. Re-run the blocked command when ready."
|
|
1405
|
+
};
|
|
1406
|
+
}
|
|
1407
|
+
function normalizeCliPaywallError(input) {
|
|
1408
|
+
const error = input.error;
|
|
1409
|
+
const status = error?.status ?? error?.statusCode;
|
|
1410
|
+
const message = error?.message ?? (input.error instanceof Error ? input.error.message : "");
|
|
1411
|
+
const body = error?.body;
|
|
1412
|
+
const candidate = getNestedError(body) ?? getNestedError(input.error) ?? {};
|
|
1413
|
+
if (stringField(candidate, "code") && stringField(candidate, "resource") && stringField(candidate, "reason") && typeof candidate.handoffRequired === "boolean") return {
|
|
1414
|
+
code: stringField(candidate, "code") ?? "PAYWALL_REQUIRED",
|
|
1415
|
+
resource: stringField(candidate, "resource") ?? input.resource,
|
|
1416
|
+
reason: stringField(candidate, "reason") ?? input.reason ?? message,
|
|
1417
|
+
requiredPlan: stringField(candidate, "requiredPlan"),
|
|
1418
|
+
pricingUrl: stringField(candidate, "pricingUrl") ?? new URL("/pricing", normalizeDinewayWebUrl(input.webUrl)).toString(),
|
|
1419
|
+
handoffRequired: candidate.handoffRequired === true
|
|
1420
|
+
};
|
|
1421
|
+
if ((status === 402 || status === 403 || status === 429) && PAYWALL_MESSAGE_PATTERN.test(message)) return {
|
|
1422
|
+
code: status === 429 ? "QUOTA_EXCEEDED" : "PAYWALL_REQUIRED",
|
|
1423
|
+
resource: input.resource,
|
|
1424
|
+
reason: input.reason ?? message,
|
|
1425
|
+
pricingUrl: new URL("/pricing", normalizeDinewayWebUrl(input.webUrl)).toString(),
|
|
1426
|
+
handoffRequired: true
|
|
1427
|
+
};
|
|
1428
|
+
return null;
|
|
1429
|
+
}
|
|
1430
|
+
async function promptEnter(message) {
|
|
1431
|
+
const readline = createInterface({
|
|
1432
|
+
input: process.stdin,
|
|
1433
|
+
output: process.stdout
|
|
1434
|
+
});
|
|
1435
|
+
try {
|
|
1436
|
+
await readline.question(message);
|
|
1437
|
+
} finally {
|
|
1438
|
+
readline.close();
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
async function createHandoff(input) {
|
|
1442
|
+
const credentials = await readStoredForgewayAccount({ platformApiUrl: input.platformApiUrl });
|
|
1443
|
+
if (!credentials) return null;
|
|
1444
|
+
const { credentials: refreshed } = await getForgewayDinewayAccount(credentials);
|
|
1445
|
+
const response = await fetch(new URL("/api/dineway-web/handoffs", input.webUrl), {
|
|
1446
|
+
method: "POST",
|
|
1447
|
+
headers: {
|
|
1448
|
+
Authorization: `Bearer ${refreshed.accessToken}`,
|
|
1449
|
+
"Content-Type": "application/json",
|
|
1450
|
+
"X-Dineway-Request": "1"
|
|
1451
|
+
},
|
|
1452
|
+
body: JSON.stringify({
|
|
1453
|
+
target: "pricing",
|
|
1454
|
+
reason: input.paywall.reason,
|
|
1455
|
+
context: {
|
|
1456
|
+
resource: input.paywall.resource,
|
|
1457
|
+
code: input.paywall.code,
|
|
1458
|
+
requiredPlan: input.paywall.requiredPlan,
|
|
1459
|
+
...input.context
|
|
1460
|
+
}
|
|
1461
|
+
})
|
|
1462
|
+
});
|
|
1463
|
+
const payload = await response.json().catch(() => null);
|
|
1464
|
+
if (!response.ok || !payload?.success || !payload.data?.url) return null;
|
|
1465
|
+
return payload.data.url;
|
|
1466
|
+
}
|
|
1467
|
+
async function handleCliPaywall(input) {
|
|
1468
|
+
const paywall = normalizeCliPaywallError(input);
|
|
1469
|
+
if (!paywall) return false;
|
|
1470
|
+
const platformApiUrl = normalizeForgewayApiUrl(input.platformApiUrl);
|
|
1471
|
+
const webUrl = normalizeDinewayWebUrl(input.webUrl ?? process.env.DINEWAY_WEB_URL);
|
|
1472
|
+
let url = paywall.pricingUrl;
|
|
1473
|
+
let handoffFailed = false;
|
|
1474
|
+
try {
|
|
1475
|
+
const handoffUrl = await createHandoff({
|
|
1476
|
+
paywall,
|
|
1477
|
+
platformApiUrl,
|
|
1478
|
+
webUrl,
|
|
1479
|
+
context: input.context
|
|
1480
|
+
});
|
|
1481
|
+
if (handoffUrl) url = handoffUrl;
|
|
1482
|
+
else handoffFailed = true;
|
|
1483
|
+
} catch {
|
|
1484
|
+
handoffFailed = true;
|
|
1485
|
+
}
|
|
1486
|
+
consola$1.error(paywall.reason);
|
|
1487
|
+
if (handoffFailed) consola$1.info(`Log in first: ${pc.cyan("dineway account login")}`);
|
|
1488
|
+
consola$1.info(`Continue in Dineway Web: ${pc.cyan(url)}`);
|
|
1489
|
+
if (!input.interactive) return true;
|
|
1490
|
+
await promptEnter("Press Enter to continue after upgrading, or Ctrl-C to cancel.");
|
|
1491
|
+
const credentials = await readStoredForgewayAccount({ platformApiUrl });
|
|
1492
|
+
if (!credentials) {
|
|
1493
|
+
consola$1.error("Still not logged in. Run: dineway account login");
|
|
1494
|
+
return true;
|
|
1495
|
+
}
|
|
1496
|
+
try {
|
|
1497
|
+
const { account } = await getForgewayDinewayAccount(credentials);
|
|
1498
|
+
const status = getCliPaywallRefreshStatus(paywall, account);
|
|
1499
|
+
if (status.blocked) consola$1.error(status.message);
|
|
1500
|
+
else consola$1.info(status.message);
|
|
1501
|
+
} catch {
|
|
1502
|
+
consola$1.error("Account is still blocked. Complete checkout or top up credits in Dineway Web.");
|
|
1503
|
+
}
|
|
1504
|
+
return true;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
955
1507
|
//#endregion
|
|
956
1508
|
//#region src/cli/commands/deploy/utils/astro-adapter.ts
|
|
957
1509
|
const NODE_ADAPTER_PACKAGE = "@astrojs/node";
|
|
@@ -989,7 +1541,7 @@ function collectUploadIncludeFiles(directory = dinewayUploadsJoin(dinewayForgewa
|
|
|
989
1541
|
\t});
|
|
990
1542
|
}
|
|
991
1543
|
`;
|
|
992
|
-
async function fileExists$
|
|
1544
|
+
async function fileExists$10(path) {
|
|
993
1545
|
try {
|
|
994
1546
|
await access(path);
|
|
995
1547
|
return true;
|
|
@@ -1150,7 +1702,7 @@ function configurePackageJson(pkg, adapter) {
|
|
|
1150
1702
|
}
|
|
1151
1703
|
async function configureAstroConfig(cwd, adapter, dryRun) {
|
|
1152
1704
|
const configPath = join(cwd, "astro.config.mjs");
|
|
1153
|
-
if (!await fileExists$
|
|
1705
|
+
if (!await fileExists$10(configPath)) return [];
|
|
1154
1706
|
const current = await readFile(configPath, "utf-8");
|
|
1155
1707
|
const next = configureAstroConfigSource(current, adapter);
|
|
1156
1708
|
if (next === current) return [{
|
|
@@ -1169,7 +1721,7 @@ async function configureAstroConfig(cwd, adapter, dryRun) {
|
|
|
1169
1721
|
}
|
|
1170
1722
|
async function configurePackage(cwd, adapter, dryRun) {
|
|
1171
1723
|
const packagePath = join(cwd, "package.json");
|
|
1172
|
-
if (!await fileExists$
|
|
1724
|
+
if (!await fileExists$10(packagePath)) return [];
|
|
1173
1725
|
const current = await readFile(packagePath, "utf-8");
|
|
1174
1726
|
const nextPkg = configurePackageJson(JSON.parse(current), adapter);
|
|
1175
1727
|
const next = JSON.stringify(nextPkg, null, " ") + "\n";
|
|
@@ -1188,7 +1740,7 @@ async function configurePackage(cwd, adapter, dryRun) {
|
|
|
1188
1740
|
}];
|
|
1189
1741
|
}
|
|
1190
1742
|
async function configureAstroDeployAdapter(cwd, adapter, dryRun = false) {
|
|
1191
|
-
if (!await fileExists$
|
|
1743
|
+
if (!await fileExists$10(join(cwd, "astro.config.mjs"))) return [];
|
|
1192
1744
|
return [...await configureAstroConfig(cwd, adapter, dryRun), ...await configurePackage(cwd, adapter, dryRun)];
|
|
1193
1745
|
}
|
|
1194
1746
|
|
|
@@ -1278,7 +1830,7 @@ volumes:
|
|
|
1278
1830
|
|
|
1279
1831
|
//#endregion
|
|
1280
1832
|
//#region src/cli/commands/deploy/targets/docker.ts
|
|
1281
|
-
async function fileExists$
|
|
1833
|
+
async function fileExists$9(path) {
|
|
1282
1834
|
try {
|
|
1283
1835
|
await access(path);
|
|
1284
1836
|
return true;
|
|
@@ -1288,7 +1840,7 @@ async function fileExists$10(path) {
|
|
|
1288
1840
|
}
|
|
1289
1841
|
async function writeGeneratedFile$2(cwd, relativePath, content, dryRun) {
|
|
1290
1842
|
const path = join(cwd, relativePath);
|
|
1291
|
-
if (await fileExists$
|
|
1843
|
+
if (await fileExists$9(path)) return {
|
|
1292
1844
|
path: relativePath,
|
|
1293
1845
|
action: "exists"
|
|
1294
1846
|
};
|
|
@@ -1378,7 +1930,7 @@ const EDGE_HYPHENS_RE$2 = /^-|-$/g;
|
|
|
1378
1930
|
const LINE_SPLIT_RE = /\r?\n/;
|
|
1379
1931
|
const DEFAULT_REGION$1 = "iad";
|
|
1380
1932
|
const FLY_VOLUME_NAME = "dineway_data";
|
|
1381
|
-
async function fileExists$
|
|
1933
|
+
async function fileExists$8(path) {
|
|
1382
1934
|
try {
|
|
1383
1935
|
await access(path);
|
|
1384
1936
|
return true;
|
|
@@ -1388,7 +1940,7 @@ async function fileExists$9(path) {
|
|
|
1388
1940
|
}
|
|
1389
1941
|
async function writeGeneratedFile$1(cwd, relativePath, content, dryRun) {
|
|
1390
1942
|
const path = join(cwd, relativePath);
|
|
1391
|
-
if (await fileExists$
|
|
1943
|
+
if (await fileExists$8(path)) return {
|
|
1392
1944
|
path: relativePath,
|
|
1393
1945
|
action: "exists"
|
|
1394
1946
|
};
|
|
@@ -1637,136 +2189,6 @@ async function writeForgewayDeployMetadata(cwd, metadata) {
|
|
|
1637
2189
|
});
|
|
1638
2190
|
}
|
|
1639
2191
|
|
|
1640
|
-
//#endregion
|
|
1641
|
-
//#region src/cli/commands/deploy/utils/forgeway-secrets.ts
|
|
1642
|
-
const DINEWAY_CONFIG_DIR_NAME = ".dineway";
|
|
1643
|
-
const DINEWAY_USER_CONFIG_DIR_NAME = "dineway";
|
|
1644
|
-
const TRAILING_SLASH_PATTERN$2 = /\/$/;
|
|
1645
|
-
function getDinewayConfigDir(cwd = process.cwd()) {
|
|
1646
|
-
return join(cwd, DINEWAY_CONFIG_DIR_NAME);
|
|
1647
|
-
}
|
|
1648
|
-
function getDinewayUserConfigDir() {
|
|
1649
|
-
return join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), DINEWAY_USER_CONFIG_DIR_NAME);
|
|
1650
|
-
}
|
|
1651
|
-
function getForgewaySecretPath(cwd = process.cwd()) {
|
|
1652
|
-
return join(getDinewayConfigDir(cwd), "forgeway.json");
|
|
1653
|
-
}
|
|
1654
|
-
function getForgewayAccountSecretPath() {
|
|
1655
|
-
return join(getDinewayUserConfigDir(), "forgeway.json");
|
|
1656
|
-
}
|
|
1657
|
-
function normalizePlatformApiUrl$1(platformApiUrl) {
|
|
1658
|
-
return platformApiUrl.replace(TRAILING_SLASH_PATTERN$2, "");
|
|
1659
|
-
}
|
|
1660
|
-
function normalizeEmail$2(email) {
|
|
1661
|
-
return email.trim().toLowerCase();
|
|
1662
|
-
}
|
|
1663
|
-
function accountKey(platformApiUrl, email) {
|
|
1664
|
-
return `${normalizePlatformApiUrl$1(platformApiUrl)}#${normalizeEmail$2(email)}`;
|
|
1665
|
-
}
|
|
1666
|
-
function normalizeLookup(lookup) {
|
|
1667
|
-
if (typeof lookup === "string") return { cwd: lookup };
|
|
1668
|
-
return lookup ?? {};
|
|
1669
|
-
}
|
|
1670
|
-
async function readStore(cwd = process.cwd()) {
|
|
1671
|
-
try {
|
|
1672
|
-
const raw = await readFile(getForgewaySecretPath(cwd), "utf-8");
|
|
1673
|
-
return JSON.parse(raw);
|
|
1674
|
-
} catch {
|
|
1675
|
-
return {};
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1678
|
-
async function writeStore(store, cwd = process.cwd()) {
|
|
1679
|
-
await mkdir(getDinewayConfigDir(cwd), {
|
|
1680
|
-
recursive: true,
|
|
1681
|
-
mode: 448
|
|
1682
|
-
});
|
|
1683
|
-
const file = getForgewaySecretPath(cwd);
|
|
1684
|
-
await writeFile(file, JSON.stringify(store, null, " ") + "\n", {
|
|
1685
|
-
encoding: "utf-8",
|
|
1686
|
-
mode: 384
|
|
1687
|
-
});
|
|
1688
|
-
await chmod(file, 384);
|
|
1689
|
-
}
|
|
1690
|
-
async function readAccountStore() {
|
|
1691
|
-
try {
|
|
1692
|
-
const raw = await readFile(getForgewayAccountSecretPath(), "utf-8");
|
|
1693
|
-
return JSON.parse(raw);
|
|
1694
|
-
} catch {
|
|
1695
|
-
return { version: 1 };
|
|
1696
|
-
}
|
|
1697
|
-
}
|
|
1698
|
-
async function writeAccountStore(store) {
|
|
1699
|
-
await mkdir(getDinewayUserConfigDir(), {
|
|
1700
|
-
recursive: true,
|
|
1701
|
-
mode: 448
|
|
1702
|
-
});
|
|
1703
|
-
const file = getForgewayAccountSecretPath();
|
|
1704
|
-
await writeFile(file, JSON.stringify({
|
|
1705
|
-
...store,
|
|
1706
|
-
version: 1
|
|
1707
|
-
}, null, " ") + "\n", {
|
|
1708
|
-
encoding: "utf-8",
|
|
1709
|
-
mode: 384
|
|
1710
|
-
});
|
|
1711
|
-
await chmod(file, 384);
|
|
1712
|
-
}
|
|
1713
|
-
function accountMatches(credentials, lookup) {
|
|
1714
|
-
if (lookup.platformApiUrl && normalizePlatformApiUrl$1(credentials.platformApiUrl) !== normalizePlatformApiUrl$1(lookup.platformApiUrl)) return false;
|
|
1715
|
-
if (lookup.email && normalizeEmail$2(credentials.user?.email ?? "") !== normalizeEmail$2(lookup.email)) return false;
|
|
1716
|
-
return true;
|
|
1717
|
-
}
|
|
1718
|
-
async function readForgewayCredentials(lookup) {
|
|
1719
|
-
const normalizedLookup = normalizeLookup(lookup);
|
|
1720
|
-
const store = await readAccountStore();
|
|
1721
|
-
const accounts = Object.values(store.accounts ?? {}).filter((credentials) => accountMatches(credentials, normalizedLookup));
|
|
1722
|
-
if (normalizedLookup.platformApiUrl && normalizedLookup.email) return store.accounts?.[accountKey(normalizedLookup.platformApiUrl, normalizedLookup.email)] ?? null;
|
|
1723
|
-
if (accounts.length === 1) return accounts[0] ?? null;
|
|
1724
|
-
return null;
|
|
1725
|
-
}
|
|
1726
|
-
async function writeForgewayCredentials(credentials, lookup) {
|
|
1727
|
-
const normalizedLookup = normalizeLookup(lookup);
|
|
1728
|
-
const platformApiUrl = normalizePlatformApiUrl$1(normalizedLookup.platformApiUrl ?? credentials.platformApiUrl);
|
|
1729
|
-
const email = normalizeEmail$2(normalizedLookup.email ?? credentials.user?.email ?? "");
|
|
1730
|
-
if (!email) throw new Error("Forgeway credentials require a user email before they can be stored.");
|
|
1731
|
-
const key = accountKey(platformApiUrl, email);
|
|
1732
|
-
const store = await readAccountStore();
|
|
1733
|
-
store.accounts = {
|
|
1734
|
-
...store.accounts,
|
|
1735
|
-
[key]: {
|
|
1736
|
-
...credentials,
|
|
1737
|
-
platformApiUrl
|
|
1738
|
-
}
|
|
1739
|
-
};
|
|
1740
|
-
store.lastAccountKey = key;
|
|
1741
|
-
await writeAccountStore(store);
|
|
1742
|
-
}
|
|
1743
|
-
async function listForgewayCredentials(platformApiUrl) {
|
|
1744
|
-
const lookup = platformApiUrl ? { platformApiUrl } : {};
|
|
1745
|
-
const store = await readAccountStore();
|
|
1746
|
-
return Object.values(store.accounts ?? {}).filter((credentials) => accountMatches(credentials, lookup));
|
|
1747
|
-
}
|
|
1748
|
-
function shadowGrantKey(platformApiUrl, placeId) {
|
|
1749
|
-
return `${platformApiUrl.replace(TRAILING_SLASH_PATTERN$2, "")}#${placeId ?? "__unbound__"}`;
|
|
1750
|
-
}
|
|
1751
|
-
async function readForgewayShadowGrant(platformApiUrl, placeId, cwd) {
|
|
1752
|
-
const store = await readStore(cwd);
|
|
1753
|
-
if (!store.shadowGrants) return null;
|
|
1754
|
-
if (placeId) return store.shadowGrants[shadowGrantKey(platformApiUrl, placeId)] ?? null;
|
|
1755
|
-
const lastKey = store.lastShadowGrantKey;
|
|
1756
|
-
if (lastKey && store.shadowGrants[lastKey]?.platformApiUrl === platformApiUrl) return store.shadowGrants[lastKey];
|
|
1757
|
-
return Object.values(store.shadowGrants).find((grant) => grant.platformApiUrl === platformApiUrl) ?? null;
|
|
1758
|
-
}
|
|
1759
|
-
async function writeForgewayShadowGrant(grant, cwd) {
|
|
1760
|
-
const store = await readStore(cwd);
|
|
1761
|
-
const key = shadowGrantKey(grant.platformApiUrl, grant.placeId);
|
|
1762
|
-
store.shadowGrants = {
|
|
1763
|
-
...store.shadowGrants,
|
|
1764
|
-
[key]: grant
|
|
1765
|
-
};
|
|
1766
|
-
store.lastShadowGrantKey = key;
|
|
1767
|
-
await writeStore(store, cwd);
|
|
1768
|
-
}
|
|
1769
|
-
|
|
1770
2192
|
//#endregion
|
|
1771
2193
|
//#region src/cli/commands/deploy/targets/forgeway.ts
|
|
1772
2194
|
const DEFAULT_DINEWAY_API_BASE_URL$1 = "https://api.dineway.ai";
|
|
@@ -1790,7 +2212,6 @@ const DINEWAY_PACKAGE_NAME = "dineway";
|
|
|
1790
2212
|
const SEMVER_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:[-+].*)?$/;
|
|
1791
2213
|
const LOCAL_PACKAGE_SPEC_PATTERN = /^(?:workspace|file|link|portal):/;
|
|
1792
2214
|
const SHADOW_EMAIL_PATTERN$1 = /^shadow_[a-f0-9]{16}@dineway\.ai$/i;
|
|
1793
|
-
const OTP_PATTERN = /^\d{6}$/;
|
|
1794
2215
|
const FOOD_TYPES = new Set([
|
|
1795
2216
|
"restaurant",
|
|
1796
2217
|
"cafe",
|
|
@@ -1850,7 +2271,7 @@ function normalizeForgewayUser$1(user) {
|
|
|
1850
2271
|
function getFetch(deps) {
|
|
1851
2272
|
return deps.fetch ?? fetch;
|
|
1852
2273
|
}
|
|
1853
|
-
async function fileExists$
|
|
2274
|
+
async function fileExists$7(path) {
|
|
1854
2275
|
try {
|
|
1855
2276
|
await access(path);
|
|
1856
2277
|
return true;
|
|
@@ -1875,7 +2296,7 @@ function formatDuration(ms) {
|
|
|
1875
2296
|
if (ms < 1e3) return `${ms}ms`;
|
|
1876
2297
|
return `${Math.round(ms / 100) / 10}s`;
|
|
1877
2298
|
}
|
|
1878
|
-
function formatBytes
|
|
2299
|
+
function formatBytes(bytes) {
|
|
1879
2300
|
if (bytes < 1024) return `${bytes} B`;
|
|
1880
2301
|
const units = [
|
|
1881
2302
|
"KB",
|
|
@@ -1925,11 +2346,6 @@ async function parseErrorResponse(response) {
|
|
|
1925
2346
|
function normalizeEmail$1(email) {
|
|
1926
2347
|
return email.trim().toLowerCase();
|
|
1927
2348
|
}
|
|
1928
|
-
function normalizeOtp(otp) {
|
|
1929
|
-
const value = otp.trim();
|
|
1930
|
-
if (!OTP_PATTERN.test(value)) throw new Error("Forgeway email verification OTP must be a 6-digit numeric code.");
|
|
1931
|
-
return value;
|
|
1932
|
-
}
|
|
1933
2349
|
function isShadowEmail$1(email) {
|
|
1934
2350
|
return Boolean(email && SHADOW_EMAIL_PATTERN$1.test(email));
|
|
1935
2351
|
}
|
|
@@ -1948,8 +2364,7 @@ async function refreshFormalAccessToken(context, deps) {
|
|
|
1948
2364
|
platformApiUrl: context.platformApiUrl,
|
|
1949
2365
|
accessToken: payload.accessToken,
|
|
1950
2366
|
refreshToken: payload.refreshToken ?? context.refreshToken,
|
|
1951
|
-
...user ? { user } : {}
|
|
1952
|
-
source: "shadow-email-upgrade"
|
|
2367
|
+
...user ? { user } : {}
|
|
1953
2368
|
};
|
|
1954
2369
|
context.accessToken = updated.accessToken;
|
|
1955
2370
|
context.refreshToken = updated.refreshToken;
|
|
@@ -1961,30 +2376,6 @@ async function refreshFormalAccessToken(context, deps) {
|
|
|
1961
2376
|
});
|
|
1962
2377
|
return payload.accessToken;
|
|
1963
2378
|
}
|
|
1964
|
-
async function refreshShadowAccessToken(context, deps) {
|
|
1965
|
-
if (!context.refreshToken) throw new Error("Dineway shadow deploy grant is missing its refresh token.");
|
|
1966
|
-
const response = await forgewayRequest(`${context.platformApiUrl}/api/auth/refresh?client_type=server`, {
|
|
1967
|
-
method: "POST",
|
|
1968
|
-
headers: { "Content-Type": "application/json" },
|
|
1969
|
-
body: JSON.stringify({ refreshToken: context.refreshToken })
|
|
1970
|
-
}, deps);
|
|
1971
|
-
if (!response.ok) throw new Error("Failed to refresh Dineway shadow deploy grant. Run dineway deploy again.");
|
|
1972
|
-
const payload = await response.json();
|
|
1973
|
-
if (!payload.accessToken || !payload.refreshToken) throw new Error("Dineway shadow refresh response was missing tokens.");
|
|
1974
|
-
context.accessToken = payload.accessToken;
|
|
1975
|
-
context.refreshToken = payload.refreshToken;
|
|
1976
|
-
await (deps.writeShadowGrant ?? writeForgewayShadowGrant)({
|
|
1977
|
-
platformApiUrl: context.platformApiUrl,
|
|
1978
|
-
placeId: context.placeId,
|
|
1979
|
-
accessToken: payload.accessToken,
|
|
1980
|
-
refreshToken: payload.refreshToken,
|
|
1981
|
-
restaurantName: context.restaurantName,
|
|
1982
|
-
city: context.city,
|
|
1983
|
-
user: payload.user ? normalizeForgewayUser$1(payload.user) : void 0,
|
|
1984
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1985
|
-
}, context.projectDir);
|
|
1986
|
-
return payload.accessToken;
|
|
1987
|
-
}
|
|
1988
2379
|
async function promptText(question, initialValue, deps) {
|
|
1989
2380
|
if (deps.promptText) return await deps.promptText(question, initialValue);
|
|
1990
2381
|
if (!process.stdin.isTTY) return "";
|
|
@@ -2047,112 +2438,17 @@ function validatePublisherEmail(rawEmail) {
|
|
|
2047
2438
|
if (!email || isShadowEmail$1(email)) throw new Error("Dineway account email must be a real owner email, not a shadow user email.");
|
|
2048
2439
|
return email;
|
|
2049
2440
|
}
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
if (
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
function isStoredFormalCredentialForEmail(stored, email) {
|
|
2057
|
-
if (!stored?.accessToken || !stored.user) return false;
|
|
2058
|
-
if (email && normalizeEmail$1(stored.user.email) !== email) return false;
|
|
2059
|
-
if (stored.user.emailVerified !== true) return false;
|
|
2060
|
-
if (isShadowEmail$1(stored.user.email)) return false;
|
|
2061
|
-
return true;
|
|
2062
|
-
}
|
|
2063
|
-
function assertVerifiedFormalAccountUser(user, expectedEmail) {
|
|
2064
|
-
if (!user || user.emailVerified !== true || isShadowEmail$1(user.email)) throw new Error("Forgeway formal account credentials require a verified formal account.");
|
|
2065
|
-
if (expectedEmail && normalizeEmail$1(user.email) !== normalizeEmail$1(expectedEmail)) throw new Error("Forgeway refresh returned credentials for a different email.");
|
|
2066
|
-
return user;
|
|
2067
|
-
}
|
|
2068
|
-
async function shadowUpgradeFetch(context, path, deps, options) {
|
|
2069
|
-
const request = async (accessToken) => await forgewayRequest(`${context.platformApiUrl}${path}`, {
|
|
2070
|
-
...options,
|
|
2071
|
-
headers: {
|
|
2072
|
-
"Content-Type": "application/json",
|
|
2073
|
-
Authorization: `Bearer ${accessToken}`,
|
|
2074
|
-
...options.headers
|
|
2075
|
-
}
|
|
2076
|
-
}, deps);
|
|
2077
|
-
if (!context.accessToken) throw new Error("Dineway shadow deploy grant is missing its access token.");
|
|
2078
|
-
let response = await request(context.accessToken);
|
|
2079
|
-
if (response.status === 401) response = await request(await refreshShadowAccessToken(context, deps));
|
|
2080
|
-
if (!response.ok) throw new ForgewayApiError(await parseErrorResponse(response), response.status);
|
|
2081
|
-
return await response.json();
|
|
2082
|
-
}
|
|
2083
|
-
async function startShadowEmailUpgrade(context, email, deps) {
|
|
2084
|
-
const payload = await shadowUpgradeFetch(context, "/api/auth/users/shadow/upgrade/email/start", deps, {
|
|
2085
|
-
method: "POST",
|
|
2086
|
-
body: JSON.stringify({ email })
|
|
2087
|
-
});
|
|
2088
|
-
if (payload.email && normalizeEmail$1(payload.email) !== email) throw new Error("Forgeway email verification response returned a different email.");
|
|
2089
|
-
return payload;
|
|
2090
|
-
}
|
|
2091
|
-
async function verifyShadowEmailUpgrade(context, email, otp, deps) {
|
|
2092
|
-
const payload = await shadowUpgradeFetch(context, "/api/auth/users/shadow/upgrade/email/verify", deps, {
|
|
2093
|
-
method: "POST",
|
|
2094
|
-
body: JSON.stringify({
|
|
2095
|
-
email,
|
|
2096
|
-
otp
|
|
2097
|
-
})
|
|
2098
|
-
});
|
|
2099
|
-
if (!payload.accessToken || !payload.refreshToken || !payload.user) throw new Error("Forgeway email upgrade response was missing credentials.");
|
|
2100
|
-
const user = normalizeForgewayUser$1(payload.user);
|
|
2101
|
-
if (normalizeEmail$1(user.email) !== email) throw new Error("Forgeway email upgrade returned credentials for a different email.");
|
|
2102
|
-
if (user.emailVerified !== true || isShadowEmail$1(user.email)) throw new Error("Forgeway email upgrade did not return a verified formal account.");
|
|
2103
|
-
return {
|
|
2104
|
-
platformApiUrl: context.platformApiUrl,
|
|
2105
|
-
accessToken: payload.accessToken,
|
|
2106
|
-
refreshToken: payload.refreshToken,
|
|
2107
|
-
user,
|
|
2108
|
-
source: "shadow-email-upgrade"
|
|
2109
|
-
};
|
|
2441
|
+
function isStoredFormalCredentialForEmail(stored, email) {
|
|
2442
|
+
if (!stored?.accessToken || !stored.user) return false;
|
|
2443
|
+
if (email && normalizeEmail$1(stored.user.email) !== email) return false;
|
|
2444
|
+
if (stored.user.emailVerified !== true) return false;
|
|
2445
|
+
if (isShadowEmail$1(stored.user.email)) return false;
|
|
2446
|
+
return true;
|
|
2110
2447
|
}
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
email
|
|
2116
|
-
});
|
|
2117
|
-
if (!options.sendOtp && !options.otp && isStoredFormalCredentialForEmail(stored, email)) return {
|
|
2118
|
-
status: "verified",
|
|
2119
|
-
credentials: {
|
|
2120
|
-
...stored,
|
|
2121
|
-
platformApiUrl: context.platformApiUrl
|
|
2122
|
-
}
|
|
2123
|
-
};
|
|
2124
|
-
if (options.sendOtp) {
|
|
2125
|
-
const response = await startShadowEmailUpgrade(context, email, deps);
|
|
2126
|
-
return {
|
|
2127
|
-
status: "otp-sent",
|
|
2128
|
-
email: response.email ? normalizeEmail$1(response.email) : email,
|
|
2129
|
-
expiresAt: response.expiresAt
|
|
2130
|
-
};
|
|
2131
|
-
}
|
|
2132
|
-
if (options.otp) {
|
|
2133
|
-
const credentials = await verifyShadowEmailUpgrade(context, email, normalizeOtp(options.otp), deps);
|
|
2134
|
-
await (deps.writeCredentials ?? writeForgewayCredentials)(credentials, {
|
|
2135
|
-
cwd: context.projectDir,
|
|
2136
|
-
platformApiUrl: context.platformApiUrl,
|
|
2137
|
-
email
|
|
2138
|
-
});
|
|
2139
|
-
return {
|
|
2140
|
-
status: "verified",
|
|
2141
|
-
credentials
|
|
2142
|
-
};
|
|
2143
|
-
}
|
|
2144
|
-
await startShadowEmailUpgrade(context, email, deps);
|
|
2145
|
-
consola.info(`Sent Forgeway email verification code to ${email}`);
|
|
2146
|
-
const credentials = await verifyShadowEmailUpgrade(context, email, await promptRequired("Email verification code: ", void 0, deps, "Email verification code is required."), deps);
|
|
2147
|
-
await (deps.writeCredentials ?? writeForgewayCredentials)(credentials, {
|
|
2148
|
-
cwd: context.projectDir,
|
|
2149
|
-
platformApiUrl: context.platformApiUrl,
|
|
2150
|
-
email
|
|
2151
|
-
});
|
|
2152
|
-
return {
|
|
2153
|
-
status: "verified",
|
|
2154
|
-
credentials
|
|
2155
|
-
};
|
|
2448
|
+
function assertVerifiedFormalAccountUser(user, expectedEmail) {
|
|
2449
|
+
if (!user || user.emailVerified !== true || isShadowEmail$1(user.email)) throw new Error("Forgeway formal account credentials require a verified formal account.");
|
|
2450
|
+
if (expectedEmail && normalizeEmail$1(user.email) !== normalizeEmail$1(expectedEmail)) throw new Error("Forgeway refresh returned credentials for a different email.");
|
|
2451
|
+
return user;
|
|
2156
2452
|
}
|
|
2157
2453
|
async function readCachedPublisherCredentials(cwd, platformApiUrl, email, deps) {
|
|
2158
2454
|
const credentials = await (deps.readCredentials ?? readForgewayCredentials)({
|
|
@@ -2218,7 +2514,6 @@ async function resolveFormalProjectContext(cwd, options, deps) {
|
|
|
2218
2514
|
const context = {
|
|
2219
2515
|
projectDir: cwd,
|
|
2220
2516
|
platformApiUrl,
|
|
2221
|
-
authKind: "formal-account",
|
|
2222
2517
|
accessToken: account.credentials.accessToken,
|
|
2223
2518
|
refreshToken: account.credentials.refreshToken,
|
|
2224
2519
|
user,
|
|
@@ -2232,7 +2527,7 @@ async function resolveFormalProjectContext(cwd, options, deps) {
|
|
|
2232
2527
|
city: options.city
|
|
2233
2528
|
};
|
|
2234
2529
|
if (!context.placeId) {
|
|
2235
|
-
const resolvedRestaurant = await
|
|
2530
|
+
const resolvedRestaurant = await resolveRequiredRestaurantInfo(options, deps);
|
|
2236
2531
|
restaurant = resolvedRestaurant;
|
|
2237
2532
|
context.placeId = await resolvePlaceIdWithFormalSearch({
|
|
2238
2533
|
context,
|
|
@@ -2248,100 +2543,11 @@ async function resolveFormalProjectContext(cwd, options, deps) {
|
|
|
2248
2543
|
restaurant
|
|
2249
2544
|
};
|
|
2250
2545
|
}
|
|
2251
|
-
async function resolveShadowProjectContext(cwd, options, deps, preferredEmail) {
|
|
2252
|
-
const saved = await readSavedForgewayMetadata(cwd);
|
|
2253
|
-
const platformApiUrl = resolvePlatformApiUrl(saved.platformApiUrl);
|
|
2254
|
-
const savedPlaceId = options.placeId ?? getEnv("DINEWAY_PLACE_ID") ?? (typeof saved.placeId === "string" ? saved.placeId : void 0);
|
|
2255
|
-
let grant = await (deps.readShadowGrant ?? readForgewayShadowGrant)(platformApiUrl, savedPlaceId, cwd);
|
|
2256
|
-
let restaurant;
|
|
2257
|
-
if (!grant) {
|
|
2258
|
-
const shadowRestaurant = await resolveShadowRestaurantInfo(options, deps);
|
|
2259
|
-
restaurant = shadowRestaurant;
|
|
2260
|
-
if (options.dryRun) throw new Error("Forgeway deploy needs a Dineway shadow deploy grant. Run without --dry-run to create it.");
|
|
2261
|
-
grant = await createShadowGrant({
|
|
2262
|
-
platformApiUrl,
|
|
2263
|
-
placeId: savedPlaceId,
|
|
2264
|
-
restaurantName: shadowRestaurant.name,
|
|
2265
|
-
city: shadowRestaurant.city,
|
|
2266
|
-
cwd,
|
|
2267
|
-
deps
|
|
2268
|
-
});
|
|
2269
|
-
} else restaurant = {
|
|
2270
|
-
name: options.restaurantName ?? grant.restaurantName,
|
|
2271
|
-
city: options.city ?? grant.city
|
|
2272
|
-
};
|
|
2273
|
-
if (!grant.placeId) {
|
|
2274
|
-
const shadowRestaurant = restaurant?.name && restaurant.city ? {
|
|
2275
|
-
name: restaurant.name,
|
|
2276
|
-
city: restaurant.city
|
|
2277
|
-
} : await resolveShadowRestaurantInfo(options, deps);
|
|
2278
|
-
restaurant = shadowRestaurant;
|
|
2279
|
-
grant = savedPlaceId ? await bindShadowGrantPlace({
|
|
2280
|
-
platformApiUrl,
|
|
2281
|
-
grant,
|
|
2282
|
-
placeId: savedPlaceId,
|
|
2283
|
-
restaurant: shadowRestaurant,
|
|
2284
|
-
cwd,
|
|
2285
|
-
deps
|
|
2286
|
-
}) : await resolvePlaceIdWithShadowSearch({
|
|
2287
|
-
platformApiUrl,
|
|
2288
|
-
grant,
|
|
2289
|
-
restaurant: shadowRestaurant,
|
|
2290
|
-
cwd,
|
|
2291
|
-
deps
|
|
2292
|
-
});
|
|
2293
|
-
}
|
|
2294
|
-
if (!grant.placeId) throw new Error("Could not resolve a Dineway place id for Forgeway deploy.");
|
|
2295
|
-
const shadowContext = {
|
|
2296
|
-
projectDir: cwd,
|
|
2297
|
-
platformApiUrl,
|
|
2298
|
-
authKind: "shadow-grant",
|
|
2299
|
-
accessToken: grant.accessToken,
|
|
2300
|
-
refreshToken: grant.refreshToken,
|
|
2301
|
-
user: grant.user,
|
|
2302
|
-
ossHost: platformApiUrl,
|
|
2303
|
-
placeId: grant.placeId,
|
|
2304
|
-
restaurantName: restaurant.name,
|
|
2305
|
-
city: restaurant.city
|
|
2306
|
-
};
|
|
2307
|
-
const credentialsResult = await resolveFormalAccountCredentials(shadowContext, await resolveForgewayAccountEmail(options, saved, deps, preferredEmail), options, deps);
|
|
2308
|
-
if (credentialsResult.status === "otp-sent") return credentialsResult;
|
|
2309
|
-
const { credentials } = credentialsResult;
|
|
2310
|
-
return {
|
|
2311
|
-
status: "ready",
|
|
2312
|
-
context: {
|
|
2313
|
-
...shadowContext,
|
|
2314
|
-
authKind: "formal-account",
|
|
2315
|
-
accessToken: credentials.accessToken,
|
|
2316
|
-
refreshToken: credentials.refreshToken,
|
|
2317
|
-
user: credentials.user,
|
|
2318
|
-
ossHost: platformApiUrl
|
|
2319
|
-
},
|
|
2320
|
-
restaurant
|
|
2321
|
-
};
|
|
2322
|
-
}
|
|
2323
2546
|
async function resolveProjectContext(cwd, options, deps) {
|
|
2324
|
-
|
|
2325
|
-
if (
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
preferredEmail = formal.email;
|
|
2329
|
-
}
|
|
2330
|
-
return await resolveShadowProjectContext(cwd, options, deps, preferredEmail);
|
|
2331
|
-
}
|
|
2332
|
-
async function createShadowUser(platformApiUrl, placeId, deps) {
|
|
2333
|
-
const response = await forgewayRequest(`${platformApiUrl}/api/auth/users/shadow`, {
|
|
2334
|
-
method: "POST",
|
|
2335
|
-
headers: { "Content-Type": "application/json" },
|
|
2336
|
-
body: JSON.stringify({
|
|
2337
|
-
...placeId ? { placeId } : {},
|
|
2338
|
-
language: "en"
|
|
2339
|
-
})
|
|
2340
|
-
}, deps);
|
|
2341
|
-
if (!response.ok) throw new Error(await parseErrorResponse(response));
|
|
2342
|
-
const payload = await response.json();
|
|
2343
|
-
if (!payload.accessToken || !payload.refreshToken) throw new Error("Dineway shadow user response was missing tokens.");
|
|
2344
|
-
return payload;
|
|
2547
|
+
const formal = await resolveFormalProjectContext(cwd, options, deps);
|
|
2548
|
+
if (formal.status !== "missing") return formal;
|
|
2549
|
+
if (formal.email) throw new Error(`No verified Dineway account credentials were found for ${formal.email}. Run: dineway account login --email ${formal.email}`);
|
|
2550
|
+
throw new Error("No verified Dineway account credentials were found. Run: dineway account login");
|
|
2345
2551
|
}
|
|
2346
2552
|
function isRecord(value) {
|
|
2347
2553
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -2522,80 +2728,6 @@ function extractPlaceCandidates(payload) {
|
|
|
2522
2728
|
if (!Array.isArray(places)) throw new Error("Dineway place search response did not include places/results.");
|
|
2523
2729
|
return places.filter(isRecord);
|
|
2524
2730
|
}
|
|
2525
|
-
function shadowContextFromGrant(cwd, platformApiUrl, grant, restaurant) {
|
|
2526
|
-
return {
|
|
2527
|
-
projectDir: cwd,
|
|
2528
|
-
platformApiUrl,
|
|
2529
|
-
authKind: "shadow-grant",
|
|
2530
|
-
accessToken: grant.accessToken,
|
|
2531
|
-
refreshToken: grant.refreshToken,
|
|
2532
|
-
user: grant.user,
|
|
2533
|
-
ossHost: platformApiUrl,
|
|
2534
|
-
placeId: grant.placeId,
|
|
2535
|
-
restaurantName: restaurant?.name ?? grant.restaurantName,
|
|
2536
|
-
city: restaurant?.city ?? grant.city
|
|
2537
|
-
};
|
|
2538
|
-
}
|
|
2539
|
-
function grantFromShadowContext(context, grant, overrides = {}) {
|
|
2540
|
-
return {
|
|
2541
|
-
...grant,
|
|
2542
|
-
platformApiUrl: context.platformApiUrl,
|
|
2543
|
-
accessToken: context.accessToken ?? grant.accessToken,
|
|
2544
|
-
refreshToken: context.refreshToken ?? grant.refreshToken,
|
|
2545
|
-
placeId: context.placeId ?? grant.placeId,
|
|
2546
|
-
restaurantName: context.restaurantName ?? grant.restaurantName,
|
|
2547
|
-
city: context.city ?? grant.city,
|
|
2548
|
-
user: context.user ?? grant.user,
|
|
2549
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2550
|
-
...overrides
|
|
2551
|
-
};
|
|
2552
|
-
}
|
|
2553
|
-
async function searchPlacesWithShadowGrant(options) {
|
|
2554
|
-
const context = shadowContextFromGrant(options.cwd, options.platformApiUrl, options.grant, options.restaurant);
|
|
2555
|
-
const payload = await shadowUpgradeFetch(context, "/api/places/search", options.deps, {
|
|
2556
|
-
method: "POST",
|
|
2557
|
-
body: JSON.stringify({ textQuery: `${options.restaurant.name} in ${options.restaurant.city}` })
|
|
2558
|
-
});
|
|
2559
|
-
return {
|
|
2560
|
-
grant: grantFromShadowContext(context, options.grant),
|
|
2561
|
-
candidates: extractPlaceCandidates(payload)
|
|
2562
|
-
};
|
|
2563
|
-
}
|
|
2564
|
-
async function bindShadowGrantPlace(options) {
|
|
2565
|
-
const context = shadowContextFromGrant(options.cwd, options.platformApiUrl, options.grant, options.restaurant);
|
|
2566
|
-
const payload = await shadowUpgradeFetch(context, "/api/auth/users/shadow/place", options.deps, {
|
|
2567
|
-
method: "POST",
|
|
2568
|
-
body: JSON.stringify({
|
|
2569
|
-
placeId: options.placeId,
|
|
2570
|
-
language: "en"
|
|
2571
|
-
})
|
|
2572
|
-
});
|
|
2573
|
-
const grant = grantFromShadowContext(context, options.grant, {
|
|
2574
|
-
placeId: options.placeId,
|
|
2575
|
-
accessToken: payload.accessToken ?? context.accessToken ?? options.grant.accessToken,
|
|
2576
|
-
refreshToken: payload.refreshToken ?? context.refreshToken ?? options.grant.refreshToken,
|
|
2577
|
-
restaurantName: options.restaurant.name,
|
|
2578
|
-
city: options.restaurant.city,
|
|
2579
|
-
user: payload.user ? normalizeForgewayUser$1(payload.user) : context.user ?? options.grant.user
|
|
2580
|
-
});
|
|
2581
|
-
await (options.deps.writeShadowGrant ?? writeForgewayShadowGrant)(grant, options.cwd);
|
|
2582
|
-
return grant;
|
|
2583
|
-
}
|
|
2584
|
-
async function resolvePlaceIdWithShadowSearch(options) {
|
|
2585
|
-
const { grant, candidates } = await searchPlacesWithShadowGrant(options);
|
|
2586
|
-
if (candidates.length === 0) throw new Error(`Dineway place search returned no candidates for "${options.restaurant.name} in ${options.restaurant.city}".`);
|
|
2587
|
-
const selected = selectPlaceCandidate(candidates, {
|
|
2588
|
-
name: options.restaurant.name,
|
|
2589
|
-
city: options.restaurant.city
|
|
2590
|
-
});
|
|
2591
|
-
const finalPlaceId = selected ? candidatePlaceId(selected.candidate) : "";
|
|
2592
|
-
if (!PLACE_ID_PATTERN.test(finalPlaceId)) throw new Error("Could not resolve a valid Dineway place id for Forgeway deploy.");
|
|
2593
|
-
return await bindShadowGrantPlace({
|
|
2594
|
-
...options,
|
|
2595
|
-
grant,
|
|
2596
|
-
placeId: finalPlaceId
|
|
2597
|
-
});
|
|
2598
|
-
}
|
|
2599
2731
|
async function resolvePlaceIdWithFormalSearch(options) {
|
|
2600
2732
|
const candidates = extractPlaceCandidates(await ossFetch(options.context, "/api/places/search", options.deps, {
|
|
2601
2733
|
method: "POST",
|
|
@@ -2610,22 +2742,6 @@ async function resolvePlaceIdWithFormalSearch(options) {
|
|
|
2610
2742
|
if (!PLACE_ID_PATTERN.test(finalPlaceId)) throw new Error("Could not resolve a valid Dineway place id for Forgeway deploy.");
|
|
2611
2743
|
return finalPlaceId;
|
|
2612
2744
|
}
|
|
2613
|
-
async function createShadowGrant(options) {
|
|
2614
|
-
if (options.placeId && !PLACE_ID_PATTERN.test(options.placeId)) throw new Error("Could not resolve a valid Dineway place id for Forgeway deploy.");
|
|
2615
|
-
const auth = await createShadowUser(options.platformApiUrl, options.placeId, options.deps);
|
|
2616
|
-
const grant = {
|
|
2617
|
-
platformApiUrl: options.platformApiUrl,
|
|
2618
|
-
...options.placeId ? { placeId: options.placeId } : {},
|
|
2619
|
-
accessToken: auth.accessToken,
|
|
2620
|
-
refreshToken: auth.refreshToken,
|
|
2621
|
-
restaurantName: options.restaurantName,
|
|
2622
|
-
city: options.city,
|
|
2623
|
-
user: auth.user ? normalizeForgewayUser$1(auth.user) : void 0,
|
|
2624
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2625
|
-
};
|
|
2626
|
-
await (options.deps.writeShadowGrant ?? writeForgewayShadowGrant)(grant, options.cwd);
|
|
2627
|
-
return grant;
|
|
2628
|
-
}
|
|
2629
2745
|
async function ossFetch(context, path, deps, options = {}) {
|
|
2630
2746
|
const request = async (token) => await forgewayRequest(`${context.ossHost.replace(TRAILING_SLASH_PATTERN$1, "")}${path}`, {
|
|
2631
2747
|
...options,
|
|
@@ -2638,7 +2754,7 @@ async function ossFetch(context, path, deps, options = {}) {
|
|
|
2638
2754
|
const token = context.accessToken;
|
|
2639
2755
|
if (!token) throw new Error("Forgeway deploy context is missing an authorization token.");
|
|
2640
2756
|
let response = await request(token);
|
|
2641
|
-
if (response.status === 401) response = await request(
|
|
2757
|
+
if (response.status === 401) response = await request(await refreshFormalAccessToken(context, deps));
|
|
2642
2758
|
if (!response.ok) throw new ForgewayApiError(await parseErrorResponse(response), response.status);
|
|
2643
2759
|
return await response.json();
|
|
2644
2760
|
}
|
|
@@ -2653,18 +2769,18 @@ function deriveForgewaySiteSlug(restaurantName, city) {
|
|
|
2653
2769
|
async function resolveSeedPath$1(cwd, explicitSeedPath) {
|
|
2654
2770
|
if (explicitSeedPath) {
|
|
2655
2771
|
const resolved = resolve(cwd, explicitSeedPath);
|
|
2656
|
-
if (!await fileExists$
|
|
2772
|
+
if (!await fileExists$7(resolved)) throw new Error(`Seed file not found: ${explicitSeedPath}`);
|
|
2657
2773
|
return resolved;
|
|
2658
2774
|
}
|
|
2659
2775
|
const convention = resolve(cwd, ".dineway", "seed.json");
|
|
2660
|
-
if (await fileExists$
|
|
2776
|
+
if (await fileExists$7(convention)) return convention;
|
|
2661
2777
|
const pkg = await readDeployPackageJson(cwd).catch(() => null);
|
|
2662
2778
|
if (typeof pkg?.dineway?.seed === "string") {
|
|
2663
2779
|
const configured = resolve(cwd, pkg.dineway.seed);
|
|
2664
|
-
if (await fileExists$
|
|
2780
|
+
if (await fileExists$7(configured)) return configured;
|
|
2665
2781
|
}
|
|
2666
2782
|
const legacyConvention = resolve(cwd, "seed", "seed.json");
|
|
2667
|
-
if (await fileExists$
|
|
2783
|
+
if (await fileExists$7(legacyConvention)) return legacyConvention;
|
|
2668
2784
|
return null;
|
|
2669
2785
|
}
|
|
2670
2786
|
async function readSeedTitle(seedPath) {
|
|
@@ -2689,7 +2805,7 @@ async function resolveRestaurantInfo(options, seedPath, deps, requireForSlug, us
|
|
|
2689
2805
|
city
|
|
2690
2806
|
};
|
|
2691
2807
|
}
|
|
2692
|
-
async function
|
|
2808
|
+
async function resolveRequiredRestaurantInfo(options, deps) {
|
|
2693
2809
|
return {
|
|
2694
2810
|
name: options.restaurantName ?? await promptRequired("Restaurant name: ", void 0, deps, "Restaurant name is required. Pass --restaurant-name."),
|
|
2695
2811
|
city: options.city ?? await promptRequired("City: ", void 0, deps, "City is required. Pass --city.")
|
|
@@ -3032,7 +3148,7 @@ async function deploySiteProject(options) {
|
|
|
3032
3148
|
consola.start("Scanning deploy files");
|
|
3033
3149
|
const localFiles = await (options.deps.collectDeploymentFiles ?? collectDeploymentFiles)(options.sourceDir);
|
|
3034
3150
|
if (localFiles.length === 0) throw new Error("No deployable files found in the source directory.");
|
|
3035
|
-
consola.success(`Scanned ${localFiles.length} files (${formatBytes
|
|
3151
|
+
consola.success(`Scanned ${localFiles.length} files (${formatBytes(deploymentFileSize(localFiles))}) in ${formatDuration(Date.now() - scanStart)}`);
|
|
3036
3152
|
const createStart = Date.now();
|
|
3037
3153
|
consola.start("Creating Forgeway deployment");
|
|
3038
3154
|
const createResult = await ossFetch(options.context, `/api/deployments/sites/${encodeURIComponent(options.siteId)}/deploy`, options.deps, {
|
|
@@ -3051,7 +3167,7 @@ async function deploySiteProject(options) {
|
|
|
3051
3167
|
const pendingFiles = createResult.files.filter((file) => !file.uploadedAt);
|
|
3052
3168
|
const pendingSize = pendingFiles.reduce((total, file) => total + file.size, 0);
|
|
3053
3169
|
if (pendingFiles.length === 0) consola.success("No file uploads needed");
|
|
3054
|
-
else consola.start(`Uploading ${pendingFiles.length} changed files (${formatBytes
|
|
3170
|
+
else consola.start(`Uploading ${pendingFiles.length} changed files (${formatBytes(pendingSize)})`);
|
|
3055
3171
|
let uploadedCount = 0;
|
|
3056
3172
|
await runWithConcurrency(pendingFiles, DIRECT_UPLOAD_CONCURRENCY, async (manifestFile) => {
|
|
3057
3173
|
const localFile = localFileByPath.get(manifestFile.path);
|
|
@@ -3094,15 +3210,6 @@ function formatDinewayAdminBootstrapMessage(bootstrap) {
|
|
|
3094
3210
|
"CLI example: npx dineway whoami"
|
|
3095
3211
|
].join("\n");
|
|
3096
3212
|
}
|
|
3097
|
-
function formatForgewayOtpSentMessage(email, expiresAt) {
|
|
3098
|
-
const lines = [
|
|
3099
|
-
`Sent Forgeway email verification code to ${email}.`,
|
|
3100
|
-
"Run the Forgeway deploy command again with the same restaurant/site options and --otp <code>.",
|
|
3101
|
-
`Example: npx dineway deploy forgeway --email ${email} --otp 123456`
|
|
3102
|
-
];
|
|
3103
|
-
if (expiresAt) lines.splice(1, 0, `Code expires at ${expiresAt}.`);
|
|
3104
|
-
return lines.join("\n");
|
|
3105
|
-
}
|
|
3106
3213
|
async function deployForgeway(cwd, options, deps = {}) {
|
|
3107
3214
|
if (options.dryRun) return { message: "[dry-run] Would resolve Forgeway project, create/reuse site, bind database, initialize Dineway, upload files, and start deployment." };
|
|
3108
3215
|
const sourceDir = resolve(cwd);
|
|
@@ -3112,9 +3219,7 @@ async function deployForgeway(cwd, options, deps = {}) {
|
|
|
3112
3219
|
if (database !== "none") await warnProjectDinewayRuntimeMismatch(cwd, deps.dinewayVersion ?? VERSION);
|
|
3113
3220
|
await ensureProjectGitignoreDinewayLocalState(cwd);
|
|
3114
3221
|
const verbose = isVerboseDeploy(options);
|
|
3115
|
-
const
|
|
3116
|
-
if (project.status === "otp-sent") return { message: formatForgewayOtpSentMessage(project.email, project.expiresAt) };
|
|
3117
|
-
const { context, restaurant } = project;
|
|
3222
|
+
const { context, restaurant } = await resolveProjectContext(cwd, options, deps);
|
|
3118
3223
|
const seedPath = options.skipSeed ? null : await resolveSeedPath$1(cwd, options.seed);
|
|
3119
3224
|
if (options.skipSeed) consola.info("Skipping seed initialization by request");
|
|
3120
3225
|
await ensureRestaurantClaim(context, restaurant, deps);
|
|
@@ -3174,7 +3279,6 @@ const forgewayTarget = {
|
|
|
3174
3279
|
remoteBuild: true,
|
|
3175
3280
|
supportLevel: "stable",
|
|
3176
3281
|
async generateConfig(cwd, options) {
|
|
3177
|
-
if (options.sendOtp) return [];
|
|
3178
3282
|
return await configureAstroDeployAdapter(cwd, "vercel", options.dryRun);
|
|
3179
3283
|
},
|
|
3180
3284
|
async deploy(cwd, options) {
|
|
@@ -3207,7 +3311,7 @@ pnpm-debug.log*
|
|
|
3207
3311
|
yarn-debug.log*
|
|
3208
3312
|
yarn-error.log*
|
|
3209
3313
|
`;
|
|
3210
|
-
async function fileExists$
|
|
3314
|
+
async function fileExists$6(path) {
|
|
3211
3315
|
try {
|
|
3212
3316
|
await access(path);
|
|
3213
3317
|
return true;
|
|
@@ -3217,7 +3321,7 @@ async function fileExists$7(path) {
|
|
|
3217
3321
|
}
|
|
3218
3322
|
async function writeGeneratedFile(cwd, relativePath, content, dryRun) {
|
|
3219
3323
|
const path = join(cwd, relativePath);
|
|
3220
|
-
if (await fileExists$
|
|
3324
|
+
if (await fileExists$6(path)) return {
|
|
3221
3325
|
path: relativePath,
|
|
3222
3326
|
action: "exists"
|
|
3223
3327
|
};
|
|
@@ -3350,7 +3454,7 @@ const gcpTarget = {
|
|
|
3350
3454
|
env: options.env,
|
|
3351
3455
|
stdio: "inherit"
|
|
3352
3456
|
});
|
|
3353
|
-
if (result.code !== 0 && !await fileExists$
|
|
3457
|
+
if (result.code !== 0 && !await fileExists$6(join(cwd, "Dockerfile"))) {
|
|
3354
3458
|
await writeFile(join(cwd, "Dockerfile"), generateDockerfile(), "utf-8");
|
|
3355
3459
|
result = await runCommand$1("gcloud", deployArgs, {
|
|
3356
3460
|
cwd,
|
|
@@ -3369,7 +3473,7 @@ const gcpTarget = {
|
|
|
3369
3473
|
|
|
3370
3474
|
//#endregion
|
|
3371
3475
|
//#region src/cli/commands/deploy/utils/build.ts
|
|
3372
|
-
async function fileExists$
|
|
3476
|
+
async function fileExists$5(path) {
|
|
3373
3477
|
try {
|
|
3374
3478
|
await access(path);
|
|
3375
3479
|
return true;
|
|
@@ -3378,19 +3482,19 @@ async function fileExists$6(path) {
|
|
|
3378
3482
|
}
|
|
3379
3483
|
}
|
|
3380
3484
|
async function detectBuildCommand(cwd) {
|
|
3381
|
-
if (await fileExists$
|
|
3485
|
+
if (await fileExists$5(join(cwd, "pnpm-lock.yaml"))) return {
|
|
3382
3486
|
packageManager: "pnpm",
|
|
3383
3487
|
command: "pnpm",
|
|
3384
3488
|
args: ["build"],
|
|
3385
3489
|
display: "pnpm build"
|
|
3386
3490
|
};
|
|
3387
|
-
if (await fileExists$
|
|
3491
|
+
if (await fileExists$5(join(cwd, "yarn.lock"))) return {
|
|
3388
3492
|
packageManager: "yarn",
|
|
3389
3493
|
command: "yarn",
|
|
3390
3494
|
args: ["build"],
|
|
3391
3495
|
display: "yarn build"
|
|
3392
3496
|
};
|
|
3393
|
-
if (await fileExists$
|
|
3497
|
+
if (await fileExists$5(join(cwd, "bun.lock")) || await fileExists$5(join(cwd, "bun.lockb"))) return {
|
|
3394
3498
|
packageManager: "bun",
|
|
3395
3499
|
command: "bun",
|
|
3396
3500
|
args: ["run", "build"],
|
|
@@ -3421,7 +3525,7 @@ const INVALID_RAILWAY_PROJECT_CHARS_RE = /[^a-z0-9-]+/g;
|
|
|
3421
3525
|
const REPEATED_HYPHENS_RE = /-+/g;
|
|
3422
3526
|
const EDGE_HYPHENS_RE = /^-|-$/g;
|
|
3423
3527
|
const RAILWAY_BUILDER = "RAILPACK";
|
|
3424
|
-
async function fileExists$
|
|
3528
|
+
async function fileExists$4(path) {
|
|
3425
3529
|
try {
|
|
3426
3530
|
await access(path);
|
|
3427
3531
|
return true;
|
|
@@ -3515,7 +3619,7 @@ const railwayTarget = {
|
|
|
3515
3619
|
async generateConfig(cwd, options) {
|
|
3516
3620
|
const generated = await configureAstroDeployAdapter(cwd, "node", options.dryRun);
|
|
3517
3621
|
const railwayPath = join(cwd, "railway.json");
|
|
3518
|
-
if (await fileExists$
|
|
3622
|
+
if (await fileExists$4(railwayPath)) return [...generated, {
|
|
3519
3623
|
path: "railway.json",
|
|
3520
3624
|
action: "exists"
|
|
3521
3625
|
}];
|
|
@@ -3706,16 +3810,6 @@ const deployCommand = defineCommand({
|
|
|
3706
3810
|
description: "Dineway account email for deploy authorization",
|
|
3707
3811
|
required: false
|
|
3708
3812
|
},
|
|
3709
|
-
"send-otp": {
|
|
3710
|
-
type: "boolean",
|
|
3711
|
-
description: "Send a Forgeway email verification code and exit without prompting or deploying",
|
|
3712
|
-
default: false
|
|
3713
|
-
},
|
|
3714
|
-
otp: {
|
|
3715
|
-
type: "string",
|
|
3716
|
-
description: "Forgeway email verification code for non-interactive deploy authorization",
|
|
3717
|
-
required: false
|
|
3718
|
-
},
|
|
3719
3813
|
"restaurant-name": {
|
|
3720
3814
|
type: "string",
|
|
3721
3815
|
description: "Restaurant name for first Forgeway site creation",
|
|
@@ -3763,8 +3857,6 @@ const deployCommand = defineCommand({
|
|
|
3763
3857
|
site: args.site,
|
|
3764
3858
|
placeId: args["place-id"],
|
|
3765
3859
|
email: args.email,
|
|
3766
|
-
sendOtp: args["send-otp"],
|
|
3767
|
-
otp: args.otp,
|
|
3768
3860
|
restaurantName: args["restaurant-name"],
|
|
3769
3861
|
city: args.city,
|
|
3770
3862
|
database: args.database,
|
|
@@ -3773,8 +3865,6 @@ const deployCommand = defineCommand({
|
|
|
3773
3865
|
forceDbInit: args["force-db-init"],
|
|
3774
3866
|
verbose: args.verbose
|
|
3775
3867
|
};
|
|
3776
|
-
if ((options.sendOtp || options.otp) && target.name !== "forgeway") throw new Error("--send-otp and --otp are only supported for Forgeway deploys.");
|
|
3777
|
-
if (options.sendOtp && options.otp) throw new Error("Use either --send-otp or --otp, not both.");
|
|
3778
3868
|
consola.start(`Preparing ${target.label} deploy`);
|
|
3779
3869
|
await ensureTargetCli(target, cwd, {
|
|
3780
3870
|
yes: options.yes,
|
|
@@ -3808,7 +3898,21 @@ const deployCommand = defineCommand({
|
|
|
3808
3898
|
consola.info("[dry-run] Would ensure Dineway local state entries in .gitignore");
|
|
3809
3899
|
consola.info(`[dry-run] Would save .dineway/deploy.json target=${target.name}`);
|
|
3810
3900
|
}
|
|
3811
|
-
const result = await target.deploy(cwd, options)
|
|
3901
|
+
const result = await target.deploy(cwd, options).catch(async (error) => {
|
|
3902
|
+
if (await handleCliPaywall({
|
|
3903
|
+
error,
|
|
3904
|
+
resource: `deploy:${target.name}`,
|
|
3905
|
+
platformApiUrl: process.env.DINEWAY_FORGEWAY_API_URL,
|
|
3906
|
+
webUrl: process.env.DINEWAY_WEB_URL,
|
|
3907
|
+
interactive: process.stdin.isTTY && !options.yes,
|
|
3908
|
+
context: {
|
|
3909
|
+
target: target.name,
|
|
3910
|
+
site: options.site,
|
|
3911
|
+
placeId: options.placeId
|
|
3912
|
+
}
|
|
3913
|
+
})) process.exit(2);
|
|
3914
|
+
throw error;
|
|
3915
|
+
});
|
|
3812
3916
|
if (result.url) consola.success(result.url);
|
|
3813
3917
|
consola.success(result.message);
|
|
3814
3918
|
}
|
|
@@ -3872,7 +3976,7 @@ async function readPackageJson$2(cwd) {
|
|
|
3872
3976
|
return null;
|
|
3873
3977
|
}
|
|
3874
3978
|
}
|
|
3875
|
-
async function fileExists$
|
|
3979
|
+
async function fileExists$3(path) {
|
|
3876
3980
|
try {
|
|
3877
3981
|
await access(path);
|
|
3878
3982
|
return true;
|
|
@@ -3919,7 +4023,7 @@ const devCommand = defineCommand({
|
|
|
3919
4023
|
}
|
|
3920
4024
|
const database = resolveCliDatabaseTarget(cwd, args.database);
|
|
3921
4025
|
consola.info(`Database: ${database.display}`);
|
|
3922
|
-
if (database.localPath && !await fileExists$
|
|
4026
|
+
if (database.localPath && !await fileExists$3(database.localPath)) consola.start("Database not found, initializing...");
|
|
3923
4027
|
const db = createDatabase({ url: database.url });
|
|
3924
4028
|
try {
|
|
3925
4029
|
consola.start("Checking database migrations...");
|
|
@@ -3958,8 +4062,8 @@ const devCommand = defineCommand({
|
|
|
3958
4062
|
"--port",
|
|
3959
4063
|
args.port
|
|
3960
4064
|
];
|
|
3961
|
-
const pnpmLockExists = await fileExists$
|
|
3962
|
-
const yarnLockExists = await fileExists$
|
|
4065
|
+
const pnpmLockExists = await fileExists$3(resolve(cwd, "pnpm-lock.yaml"));
|
|
4066
|
+
const yarnLockExists = await fileExists$3(resolve(cwd, "yarn.lock"));
|
|
3963
4067
|
let cmd;
|
|
3964
4068
|
let cmdArgs;
|
|
3965
4069
|
if (pnpmLockExists) {
|
|
@@ -4003,7 +4107,7 @@ const devCommand = defineCommand({
|
|
|
4003
4107
|
*
|
|
4004
4108
|
* Diagnose database health: connection, migrations, schema integrity.
|
|
4005
4109
|
*/
|
|
4006
|
-
async function fileExists$
|
|
4110
|
+
async function fileExists$2(path) {
|
|
4007
4111
|
try {
|
|
4008
4112
|
await access(path);
|
|
4009
4113
|
return true;
|
|
@@ -4016,7 +4120,7 @@ function printResult(result) {
|
|
|
4016
4120
|
}
|
|
4017
4121
|
async function checkDatabase(database) {
|
|
4018
4122
|
const results = [];
|
|
4019
|
-
if (database.localPath && !await fileExists$
|
|
4123
|
+
if (database.localPath && !await fileExists$2(database.localPath)) {
|
|
4020
4124
|
results.push({
|
|
4021
4125
|
name: "database",
|
|
4022
4126
|
status: "fail",
|
|
@@ -4070,7 +4174,7 @@ async function checkDatabase(database) {
|
|
|
4070
4174
|
});
|
|
4071
4175
|
} catch {}
|
|
4072
4176
|
try {
|
|
4073
|
-
const count = (await sql`SELECT COUNT(id) as count FROM
|
|
4177
|
+
const count = (await sql`SELECT COUNT(id) as count FROM users`.execute(db)).rows[0]?.count ?? 0;
|
|
4074
4178
|
results.push({
|
|
4075
4179
|
name: "users",
|
|
4076
4180
|
status: count > 0 ? "pass" : "warn",
|
|
@@ -4218,7 +4322,7 @@ async function exportSeed(db, withContent) {
|
|
|
4218
4322
|
const { bylines, idToSeedId: bylineIdToSeedId } = await exportBylines(db);
|
|
4219
4323
|
if (bylines.length > 0) seed.bylines = bylines;
|
|
4220
4324
|
if (withContent !== void 0) {
|
|
4221
|
-
const collections = withContent === "" || withContent === "true" ? null : withContent.split(",").map((s) => s.trim()).filter(Boolean);
|
|
4325
|
+
const collections = withContent === "" || withContent === "true" || withContent === "all" ? null : withContent.split(",").map((s) => s.trim()).filter(Boolean);
|
|
4222
4326
|
seed.content = await exportContent(db, seed.collections || [], collections, bylineIdToSeedId, i18nEnabled);
|
|
4223
4327
|
}
|
|
4224
4328
|
return seed;
|
|
@@ -4348,8 +4452,8 @@ async function exportTaxonomies(db, i18nEnabled) {
|
|
|
4348
4452
|
for (const def of defs) {
|
|
4349
4453
|
const defSeedId = i18nEnabled && def.locale ? `tax:${def.name}:${def.locale}` : `tax:${def.name}`;
|
|
4350
4454
|
const terms = await termRepo.findByName(def.name, { locale: def.locale });
|
|
4351
|
-
const
|
|
4352
|
-
for (const term of terms)
|
|
4455
|
+
const groupToSlug = /* @__PURE__ */ new Map();
|
|
4456
|
+
for (const term of terms) groupToSlug.set(term.translationGroup ?? term.id, term.slug);
|
|
4353
4457
|
const termGroupToSeedId = /* @__PURE__ */ new Map();
|
|
4354
4458
|
const seedTerms = [];
|
|
4355
4459
|
for (const term of terms) {
|
|
@@ -4360,7 +4464,7 @@ async function exportTaxonomies(db, i18nEnabled) {
|
|
|
4360
4464
|
label: term.label,
|
|
4361
4465
|
description: typeof term.data?.description === "string" ? term.data.description : void 0
|
|
4362
4466
|
};
|
|
4363
|
-
if (term.parentId) seedTerm.parent =
|
|
4467
|
+
if (term.parentId) seedTerm.parent = groupToSlug.get(term.parentId);
|
|
4364
4468
|
if (i18nEnabled && term.locale) {
|
|
4365
4469
|
seedTerm.locale = term.locale;
|
|
4366
4470
|
if (term.translationGroup) {
|
|
@@ -4649,7 +4753,7 @@ async function getTaxonomyAssignments(taxonomyRepo, collection, entryId) {
|
|
|
4649
4753
|
*
|
|
4650
4754
|
* Initialize database from template config in package.json
|
|
4651
4755
|
*/
|
|
4652
|
-
async function fileExists$
|
|
4756
|
+
async function fileExists$1(path) {
|
|
4653
4757
|
try {
|
|
4654
4758
|
await access(path);
|
|
4655
4759
|
return true;
|
|
@@ -4749,7 +4853,7 @@ const initCommand = defineCommand({
|
|
|
4749
4853
|
if (alreadyInitialized && args.force) consola.warn("Re-initializing (--force)...");
|
|
4750
4854
|
if (config?.schema) {
|
|
4751
4855
|
const schemaPath = resolve(cwd, config.schema);
|
|
4752
|
-
if (await fileExists$
|
|
4856
|
+
if (await fileExists$1(schemaPath)) {
|
|
4753
4857
|
consola.start(`Running schema: ${config.schema}`);
|
|
4754
4858
|
try {
|
|
4755
4859
|
await runSqlFile(db, schemaPath);
|
|
@@ -5292,593 +5396,6 @@ const menuCommand = defineCommand({
|
|
|
5292
5396
|
}
|
|
5293
5397
|
});
|
|
5294
5398
|
|
|
5295
|
-
//#endregion
|
|
5296
|
-
//#region src/cli/commands/bundle-utils.ts
|
|
5297
|
-
/**
|
|
5298
|
-
* Bundle utility functions
|
|
5299
|
-
*
|
|
5300
|
-
* Shared logic extracted from the bundle command so it can be tested
|
|
5301
|
-
* without the CLI harness and tsdown dependency.
|
|
5302
|
-
*/
|
|
5303
|
-
const MAX_BUNDLE_SIZE = 256 * 1024;
|
|
5304
|
-
const MAX_FILE_SIZE = 128 * 1024;
|
|
5305
|
-
const MAX_FILE_COUNT = 20;
|
|
5306
|
-
const MAX_SCREENSHOTS = 5;
|
|
5307
|
-
const MAX_SCREENSHOT_WIDTH = 1920;
|
|
5308
|
-
const MAX_SCREENSHOT_HEIGHT = 1080;
|
|
5309
|
-
const ICON_SIZE = 256;
|
|
5310
|
-
function toFileImportSpecifier(filePath) {
|
|
5311
|
-
return pathToFileURL(filePath).href;
|
|
5312
|
-
}
|
|
5313
|
-
/** Matches Node.js built-in imports in bundled output. */
|
|
5314
|
-
const NODE_BUILTIN_IMPORT_RE = /(?:import|export|require)\s*(?:\(|[^(]*?\bfrom\s+)["'](?:node:)?([a-z_]+)(?:\/[^"']*)?\s*["']\)?/g;
|
|
5315
|
-
const LEADING_DOT_SLASH_RE = /^\.\//;
|
|
5316
|
-
const DIST_PREFIX_RE = /^dist\//;
|
|
5317
|
-
const MJS_EXT_RE = /\.m?js$/;
|
|
5318
|
-
const BUILD_OUTPUT_EXT_RE = /\.(?:mjs|js|cjs)$/;
|
|
5319
|
-
const TS_TO_TSX_RE = /\.ts$/;
|
|
5320
|
-
/** Node.js built-in modules that shouldn't appear in sandbox code */
|
|
5321
|
-
const NODE_BUILTINS = new Set([
|
|
5322
|
-
"assert",
|
|
5323
|
-
"buffer",
|
|
5324
|
-
"child_process",
|
|
5325
|
-
"cluster",
|
|
5326
|
-
"crypto",
|
|
5327
|
-
"dgram",
|
|
5328
|
-
"dns",
|
|
5329
|
-
"domain",
|
|
5330
|
-
"events",
|
|
5331
|
-
"fs",
|
|
5332
|
-
"http",
|
|
5333
|
-
"http2",
|
|
5334
|
-
"https",
|
|
5335
|
-
"inspector",
|
|
5336
|
-
"module",
|
|
5337
|
-
"net",
|
|
5338
|
-
"os",
|
|
5339
|
-
"path",
|
|
5340
|
-
"perf_hooks",
|
|
5341
|
-
"process",
|
|
5342
|
-
"punycode",
|
|
5343
|
-
"querystring",
|
|
5344
|
-
"readline",
|
|
5345
|
-
"repl",
|
|
5346
|
-
"stream",
|
|
5347
|
-
"string_decoder",
|
|
5348
|
-
"sys",
|
|
5349
|
-
"timers",
|
|
5350
|
-
"tls",
|
|
5351
|
-
"trace_events",
|
|
5352
|
-
"tty",
|
|
5353
|
-
"url",
|
|
5354
|
-
"util",
|
|
5355
|
-
"v8",
|
|
5356
|
-
"vm",
|
|
5357
|
-
"wasi",
|
|
5358
|
-
"worker_threads",
|
|
5359
|
-
"zlib"
|
|
5360
|
-
]);
|
|
5361
|
-
async function fileExists$1(path) {
|
|
5362
|
-
try {
|
|
5363
|
-
await access(path);
|
|
5364
|
-
return true;
|
|
5365
|
-
} catch {
|
|
5366
|
-
return false;
|
|
5367
|
-
}
|
|
5368
|
-
}
|
|
5369
|
-
/**
|
|
5370
|
-
* Read image dimensions from a buffer.
|
|
5371
|
-
* Returns [width, height] or null if the format is unrecognized.
|
|
5372
|
-
*/
|
|
5373
|
-
function readImageDimensions(buf) {
|
|
5374
|
-
try {
|
|
5375
|
-
const result = imageSize(buf);
|
|
5376
|
-
if (result.width != null && result.height != null) return [result.width, result.height];
|
|
5377
|
-
return null;
|
|
5378
|
-
} catch {
|
|
5379
|
-
return null;
|
|
5380
|
-
}
|
|
5381
|
-
}
|
|
5382
|
-
/**
|
|
5383
|
-
* Extract manifest metadata from a ResolvedPlugin.
|
|
5384
|
-
* Strips functions (hooks, route handlers) and keeps only serializable metadata.
|
|
5385
|
-
*/
|
|
5386
|
-
function extractManifest(plugin) {
|
|
5387
|
-
const hooks = [];
|
|
5388
|
-
for (const [name, resolved] of Object.entries(plugin.hooks)) {
|
|
5389
|
-
if (!resolved) continue;
|
|
5390
|
-
if (resolved.exclusive || resolved.priority !== 100 || resolved.timeout !== 5e3) {
|
|
5391
|
-
const entry = { name };
|
|
5392
|
-
if (resolved.exclusive) entry.exclusive = true;
|
|
5393
|
-
if (resolved.priority !== 100) entry.priority = resolved.priority;
|
|
5394
|
-
if (resolved.timeout !== 5e3) entry.timeout = resolved.timeout;
|
|
5395
|
-
hooks.push(entry);
|
|
5396
|
-
} else hooks.push(name);
|
|
5397
|
-
}
|
|
5398
|
-
return {
|
|
5399
|
-
id: plugin.id,
|
|
5400
|
-
version: plugin.version,
|
|
5401
|
-
capabilities: plugin.capabilities,
|
|
5402
|
-
allowedHosts: plugin.allowedHosts,
|
|
5403
|
-
storage: plugin.storage,
|
|
5404
|
-
hooks,
|
|
5405
|
-
routes: Object.keys(plugin.routes),
|
|
5406
|
-
admin: {
|
|
5407
|
-
settingsSchema: plugin.admin.settingsSchema,
|
|
5408
|
-
pages: plugin.admin.pages,
|
|
5409
|
-
widgets: plugin.admin.widgets
|
|
5410
|
-
}
|
|
5411
|
-
};
|
|
5412
|
-
}
|
|
5413
|
-
/**
|
|
5414
|
-
* Scan bundled code for Node.js built-in imports.
|
|
5415
|
-
* Matches require("node:xxx"), require("xxx"), import("node:xxx") — the patterns
|
|
5416
|
-
* that appear in bundled ESM/CJS output (not source-level named imports).
|
|
5417
|
-
* Returns deduplicated array of built-in module names found.
|
|
5418
|
-
*/
|
|
5419
|
-
function findNodeBuiltinImports(code) {
|
|
5420
|
-
const found = [];
|
|
5421
|
-
NODE_BUILTIN_IMPORT_RE.lastIndex = 0;
|
|
5422
|
-
let match;
|
|
5423
|
-
while ((match = NODE_BUILTIN_IMPORT_RE.exec(code)) !== null) {
|
|
5424
|
-
const mod = match[1];
|
|
5425
|
-
if (NODE_BUILTINS.has(mod)) found.push(mod);
|
|
5426
|
-
}
|
|
5427
|
-
return [...new Set(found)];
|
|
5428
|
-
}
|
|
5429
|
-
/**
|
|
5430
|
-
* Find a build output file by base name, checking common extensions.
|
|
5431
|
-
* tsdown may output .mjs, .js, or .cjs depending on format and config.
|
|
5432
|
-
*/
|
|
5433
|
-
async function findBuildOutput(dir, baseName) {
|
|
5434
|
-
const normalizedBaseName = baseName.replace(BUILD_OUTPUT_EXT_RE, "");
|
|
5435
|
-
for (const ext of [
|
|
5436
|
-
".mjs",
|
|
5437
|
-
".js",
|
|
5438
|
-
".cjs"
|
|
5439
|
-
]) {
|
|
5440
|
-
const candidate = join(dir, `${normalizedBaseName}${ext}`);
|
|
5441
|
-
if (await fileExists$1(candidate)) return candidate;
|
|
5442
|
-
}
|
|
5443
|
-
}
|
|
5444
|
-
/**
|
|
5445
|
-
* Resolve a dist/built path back to its source .ts/.tsx equivalent.
|
|
5446
|
-
* E.g., "./dist/index.mjs" → "src/index.ts"
|
|
5447
|
-
*/
|
|
5448
|
-
async function resolveSourceEntry(pluginDir, distPath) {
|
|
5449
|
-
const cleaned = distPath.replace(LEADING_DOT_SLASH_RE, "");
|
|
5450
|
-
const srcPath = cleaned.replace(DIST_PREFIX_RE, "src/").replace(MJS_EXT_RE, ".ts");
|
|
5451
|
-
const srcFull = resolve(pluginDir, srcPath);
|
|
5452
|
-
if (await fileExists$1(srcFull)) return srcFull;
|
|
5453
|
-
const tsxFull = resolve(pluginDir, srcPath.replace(TS_TO_TSX_RE, ".tsx"));
|
|
5454
|
-
if (await fileExists$1(tsxFull)) return tsxFull;
|
|
5455
|
-
const direct = resolve(pluginDir, cleaned);
|
|
5456
|
-
if (await fileExists$1(direct)) return direct;
|
|
5457
|
-
}
|
|
5458
|
-
const TS_SOURCE_EXPORT_RE = /\.(?:ts|tsx|mts|cts|jsx)$/;
|
|
5459
|
-
/**
|
|
5460
|
-
* Find package.json exports that point to source files instead of built output.
|
|
5461
|
-
* Returns an array of `{ exportPath, resolvedPath }` for each offending export.
|
|
5462
|
-
*/
|
|
5463
|
-
function findSourceExports(exports) {
|
|
5464
|
-
const issues = [];
|
|
5465
|
-
for (const [exportPath, exportValue] of Object.entries(exports)) {
|
|
5466
|
-
const resolved = typeof exportValue === "string" ? exportValue : exportValue && typeof exportValue === "object" && "import" in exportValue ? exportValue.import : null;
|
|
5467
|
-
if (resolved && TS_SOURCE_EXPORT_RE.test(resolved)) issues.push({
|
|
5468
|
-
exportPath,
|
|
5469
|
-
resolvedPath: resolved
|
|
5470
|
-
});
|
|
5471
|
-
}
|
|
5472
|
-
return issues;
|
|
5473
|
-
}
|
|
5474
|
-
/**
|
|
5475
|
-
* Recursively walk a staging directory and return all regular files with
|
|
5476
|
-
* tarball-relative names.
|
|
5477
|
-
*/
|
|
5478
|
-
async function collectBundleEntries(dir) {
|
|
5479
|
-
const entries = [];
|
|
5480
|
-
await walkBundle(dir, "", entries);
|
|
5481
|
-
return entries;
|
|
5482
|
-
}
|
|
5483
|
-
async function walkBundle(dir, prefix, into) {
|
|
5484
|
-
const items = await readdir(dir, { withFileTypes: true });
|
|
5485
|
-
for (const item of items) {
|
|
5486
|
-
const fullPath = join(dir, item.name);
|
|
5487
|
-
const relPath = prefix ? `${prefix}/${item.name}` : item.name;
|
|
5488
|
-
if (item.isFile()) {
|
|
5489
|
-
const s = await stat(fullPath);
|
|
5490
|
-
into.push({
|
|
5491
|
-
name: relPath,
|
|
5492
|
-
bytes: s.size
|
|
5493
|
-
});
|
|
5494
|
-
} else if (item.isDirectory()) await walkBundle(fullPath, relPath, into);
|
|
5495
|
-
}
|
|
5496
|
-
}
|
|
5497
|
-
function totalBundleBytes(entries) {
|
|
5498
|
-
let total = 0;
|
|
5499
|
-
for (const entry of entries) total += entry.bytes;
|
|
5500
|
-
return total;
|
|
5501
|
-
}
|
|
5502
|
-
/**
|
|
5503
|
-
* Enforce sandboxed plugin caps against decompressed bundle files:
|
|
5504
|
-
* total size, per-file size, and file count.
|
|
5505
|
-
*/
|
|
5506
|
-
function validateBundleSize(entries) {
|
|
5507
|
-
const violations = [];
|
|
5508
|
-
const total = totalBundleBytes(entries);
|
|
5509
|
-
if (total > MAX_BUNDLE_SIZE) violations.push(`Bundle size ${formatBytes(total)} exceeds maximum of ${formatBytes(MAX_BUNDLE_SIZE)}.`);
|
|
5510
|
-
if (entries.length > MAX_FILE_COUNT) violations.push(`Bundle contains ${entries.length} files, exceeds maximum of ${MAX_FILE_COUNT}.`);
|
|
5511
|
-
const oversized = entries.filter((entry) => entry.bytes > MAX_FILE_SIZE).toSorted((a, b) => a.name.localeCompare(b.name));
|
|
5512
|
-
for (const entry of oversized) violations.push(`File ${entry.name} is ${formatBytes(entry.bytes)}, exceeds per-file maximum of ${formatBytes(MAX_FILE_SIZE)}.`);
|
|
5513
|
-
return violations;
|
|
5514
|
-
}
|
|
5515
|
-
function formatBytes(bytes) {
|
|
5516
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
5517
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
5518
|
-
return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
|
|
5519
|
-
}
|
|
5520
|
-
/**
|
|
5521
|
-
* Create a gzipped tarball from a directory.
|
|
5522
|
-
*/
|
|
5523
|
-
async function createTarball(sourceDir, outputPath) {
|
|
5524
|
-
const { createGzip } = await import("node:zlib");
|
|
5525
|
-
await pipeline(packTar(sourceDir), createGzip({ level: 9 }), createWriteStream(outputPath));
|
|
5526
|
-
}
|
|
5527
|
-
|
|
5528
|
-
//#endregion
|
|
5529
|
-
//#region src/cli/commands/bundle.ts
|
|
5530
|
-
/**
|
|
5531
|
-
* dineway plugin bundle
|
|
5532
|
-
*
|
|
5533
|
-
* Produces a publishable plugin tarball from a plugin source directory.
|
|
5534
|
-
*
|
|
5535
|
-
* Steps:
|
|
5536
|
-
* 1. Resolve plugin entrypoint (finds definePlugin() export)
|
|
5537
|
-
* 2. Bundle backend code with tsdown → backend.js (single ES module, tree-shaken)
|
|
5538
|
-
* 3. Bundle admin code if present → admin.js
|
|
5539
|
-
* 4. Extract manifest from definePlugin() → manifest.json
|
|
5540
|
-
* 5. Collect assets (README.md, icon.png, screenshots/)
|
|
5541
|
-
* 6. Validate bundle (manifest schema, size limits, no Node.js builtins)
|
|
5542
|
-
* 7. Create tarball ({id}-{version}.tar.gz)
|
|
5543
|
-
*/
|
|
5544
|
-
var bundle_exports = /* @__PURE__ */ __exportAll({ bundleCommand: () => bundleCommand });
|
|
5545
|
-
const TS_EXT_RE = /\.tsx?$/;
|
|
5546
|
-
const SLASH_RE = /\//g;
|
|
5547
|
-
const LEADING_AT_RE = /^@/;
|
|
5548
|
-
const DINEWAY_SCOPE_RE = /^@dineway-ai\//;
|
|
5549
|
-
const bundleCommand = defineCommand({
|
|
5550
|
-
meta: {
|
|
5551
|
-
name: "bundle",
|
|
5552
|
-
description: "Bundle a plugin for marketplace distribution"
|
|
5553
|
-
},
|
|
5554
|
-
args: {
|
|
5555
|
-
dir: {
|
|
5556
|
-
type: "string",
|
|
5557
|
-
description: "Plugin directory (default: current directory)",
|
|
5558
|
-
default: process.cwd()
|
|
5559
|
-
},
|
|
5560
|
-
outDir: {
|
|
5561
|
-
type: "string",
|
|
5562
|
-
alias: "o",
|
|
5563
|
-
description: "Output directory for the tarball (default: ./dist)",
|
|
5564
|
-
default: "dist"
|
|
5565
|
-
},
|
|
5566
|
-
validateOnly: {
|
|
5567
|
-
type: "boolean",
|
|
5568
|
-
description: "Run validation only, skip tarball creation",
|
|
5569
|
-
default: false
|
|
5570
|
-
}
|
|
5571
|
-
},
|
|
5572
|
-
async run({ args }) {
|
|
5573
|
-
const pluginDir = resolve(args.dir);
|
|
5574
|
-
const outDir = resolve(pluginDir, args.outDir);
|
|
5575
|
-
const validateOnly = args.validateOnly;
|
|
5576
|
-
consola.start(validateOnly ? "Validating plugin..." : "Bundling plugin...");
|
|
5577
|
-
const pkgPath = join(pluginDir, "package.json");
|
|
5578
|
-
if (!await fileExists$1(pkgPath)) {
|
|
5579
|
-
consola.error("No package.json found in", pluginDir);
|
|
5580
|
-
process.exit(1);
|
|
5581
|
-
}
|
|
5582
|
-
const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
|
|
5583
|
-
let backendEntry;
|
|
5584
|
-
let adminEntry;
|
|
5585
|
-
if (pkg.exports) {
|
|
5586
|
-
const sandboxExport = pkg.exports["./sandbox"];
|
|
5587
|
-
if (typeof sandboxExport === "string") backendEntry = await resolveSourceEntry(pluginDir, sandboxExport);
|
|
5588
|
-
else if (sandboxExport && typeof sandboxExport === "object" && "import" in sandboxExport) backendEntry = await resolveSourceEntry(pluginDir, sandboxExport.import);
|
|
5589
|
-
const adminExport = pkg.exports["./admin"];
|
|
5590
|
-
if (typeof adminExport === "string") adminEntry = await resolveSourceEntry(pluginDir, adminExport);
|
|
5591
|
-
else if (adminExport && typeof adminExport === "object" && "import" in adminExport) adminEntry = await resolveSourceEntry(pluginDir, adminExport.import);
|
|
5592
|
-
}
|
|
5593
|
-
if (!backendEntry) {
|
|
5594
|
-
const defaultSandbox = join(pluginDir, "src/sandbox-entry.ts");
|
|
5595
|
-
if (await fileExists$1(defaultSandbox)) backendEntry = defaultSandbox;
|
|
5596
|
-
}
|
|
5597
|
-
let mainEntry;
|
|
5598
|
-
if (pkg.exports?.["."] !== void 0) {
|
|
5599
|
-
const mainExport = pkg.exports["."];
|
|
5600
|
-
if (typeof mainExport === "string") mainEntry = await resolveSourceEntry(pluginDir, mainExport);
|
|
5601
|
-
else if (mainExport && typeof mainExport === "object" && "import" in mainExport) mainEntry = await resolveSourceEntry(pluginDir, mainExport.import);
|
|
5602
|
-
}
|
|
5603
|
-
if (!mainEntry && pkg.main) mainEntry = await resolveSourceEntry(pluginDir, pkg.main);
|
|
5604
|
-
if (!mainEntry) {
|
|
5605
|
-
const defaultMain = join(pluginDir, "src/index.ts");
|
|
5606
|
-
if (await fileExists$1(defaultMain)) mainEntry = defaultMain;
|
|
5607
|
-
}
|
|
5608
|
-
if (!mainEntry) {
|
|
5609
|
-
consola.error("Cannot find plugin entrypoint. Expected src/index.ts or main/exports in package.json");
|
|
5610
|
-
process.exit(1);
|
|
5611
|
-
}
|
|
5612
|
-
consola.info(`Main entry: ${mainEntry}`);
|
|
5613
|
-
if (backendEntry) consola.info(`Backend entry: ${backendEntry}`);
|
|
5614
|
-
if (adminEntry) consola.info(`Admin entry: ${adminEntry}`);
|
|
5615
|
-
consola.start("Extracting plugin manifest...");
|
|
5616
|
-
const { build } = await import("tsdown");
|
|
5617
|
-
const tmpDir = join(pluginDir, ".dineway-bundle-tmp");
|
|
5618
|
-
try {
|
|
5619
|
-
await mkdir(tmpDir, { recursive: true });
|
|
5620
|
-
await copyFile(pkgPath, join(tmpDir, "package.json"));
|
|
5621
|
-
const mainOutDir = join(tmpDir, "main");
|
|
5622
|
-
await build({
|
|
5623
|
-
config: false,
|
|
5624
|
-
entry: [mainEntry],
|
|
5625
|
-
format: "esm",
|
|
5626
|
-
outDir: mainOutDir,
|
|
5627
|
-
dts: false,
|
|
5628
|
-
platform: "node",
|
|
5629
|
-
external: ["dineway", DINEWAY_SCOPE_RE]
|
|
5630
|
-
});
|
|
5631
|
-
const pluginNodeModules = join(pluginDir, "node_modules");
|
|
5632
|
-
const tmpNodeModules = join(mainOutDir, "node_modules");
|
|
5633
|
-
if (await fileExists$1(pluginNodeModules)) await symlink(pluginNodeModules, tmpNodeModules, "junction");
|
|
5634
|
-
const mainOutputPath = await findBuildOutput(mainOutDir, basename(mainEntry).replace(TS_EXT_RE, ""));
|
|
5635
|
-
if (!mainOutputPath) {
|
|
5636
|
-
consola.error("Failed to build main entry — no output found in", mainOutDir);
|
|
5637
|
-
process.exit(1);
|
|
5638
|
-
}
|
|
5639
|
-
const pluginModule = await import(toFileImportSpecifier(mainOutputPath));
|
|
5640
|
-
let resolvedPlugin;
|
|
5641
|
-
if (typeof pluginModule.createPlugin === "function") resolvedPlugin = pluginModule.createPlugin();
|
|
5642
|
-
else if (typeof pluginModule.default === "function") resolvedPlugin = pluginModule.default();
|
|
5643
|
-
else if (typeof pluginModule.default === "object" && pluginModule.default !== null) {
|
|
5644
|
-
const defaultExport = pluginModule.default;
|
|
5645
|
-
if ("id" in defaultExport && "version" in defaultExport) resolvedPlugin = defaultExport;
|
|
5646
|
-
}
|
|
5647
|
-
if (!resolvedPlugin) for (const [key, value] of Object.entries(pluginModule)) {
|
|
5648
|
-
if (key === "default" || typeof value !== "function") continue;
|
|
5649
|
-
try {
|
|
5650
|
-
const result = value();
|
|
5651
|
-
if (result && typeof result === "object" && "id" in result && "version" in result) {
|
|
5652
|
-
resolvedPlugin = {
|
|
5653
|
-
id: result.id,
|
|
5654
|
-
version: result.version,
|
|
5655
|
-
capabilities: result.capabilities ?? [],
|
|
5656
|
-
allowedHosts: result.allowedHosts ?? [],
|
|
5657
|
-
storage: result.storage ?? {},
|
|
5658
|
-
hooks: {},
|
|
5659
|
-
routes: {},
|
|
5660
|
-
admin: {
|
|
5661
|
-
pages: result.adminPages,
|
|
5662
|
-
widgets: result.adminWidgets
|
|
5663
|
-
}
|
|
5664
|
-
};
|
|
5665
|
-
if (backendEntry) {
|
|
5666
|
-
const backendProbeDir = join(tmpDir, "backend-probe");
|
|
5667
|
-
const probeShimDir = join(tmpDir, "probe-shims");
|
|
5668
|
-
await mkdir(probeShimDir, { recursive: true });
|
|
5669
|
-
await writeFile(join(probeShimDir, "dineway.mjs"), "export const definePlugin = (d) => d;\n");
|
|
5670
|
-
await build({
|
|
5671
|
-
config: false,
|
|
5672
|
-
entry: [backendEntry],
|
|
5673
|
-
format: "esm",
|
|
5674
|
-
outDir: backendProbeDir,
|
|
5675
|
-
dts: false,
|
|
5676
|
-
platform: "neutral",
|
|
5677
|
-
external: [],
|
|
5678
|
-
alias: { dineway: join(probeShimDir, "dineway.mjs") },
|
|
5679
|
-
treeshake: true
|
|
5680
|
-
});
|
|
5681
|
-
const backendProbePath = await findBuildOutput(backendProbeDir, basename(backendEntry).replace(TS_EXT_RE, ""));
|
|
5682
|
-
if (backendProbePath) {
|
|
5683
|
-
const standardDef = (await import(toFileImportSpecifier(backendProbePath))).default ?? {};
|
|
5684
|
-
const hooks = standardDef.hooks;
|
|
5685
|
-
const routes = standardDef.routes;
|
|
5686
|
-
if (hooks) for (const hookName of Object.keys(hooks)) {
|
|
5687
|
-
const hookEntry = hooks[hookName];
|
|
5688
|
-
const isConfig = typeof hookEntry === "object" && hookEntry !== null && "handler" in hookEntry;
|
|
5689
|
-
const config = isConfig ? hookEntry : {};
|
|
5690
|
-
resolvedPlugin.hooks[hookName] = {
|
|
5691
|
-
handler: isConfig ? hookEntry.handler : hookEntry,
|
|
5692
|
-
priority: config.priority ?? 100,
|
|
5693
|
-
timeout: config.timeout ?? 5e3,
|
|
5694
|
-
dependencies: config.dependencies ?? [],
|
|
5695
|
-
errorPolicy: config.errorPolicy ?? "abort",
|
|
5696
|
-
exclusive: config.exclusive ?? false,
|
|
5697
|
-
pluginId: result.id
|
|
5698
|
-
};
|
|
5699
|
-
}
|
|
5700
|
-
if (routes) for (const [name, route] of Object.entries(routes)) {
|
|
5701
|
-
const routeObj = route;
|
|
5702
|
-
resolvedPlugin.routes[name] = {
|
|
5703
|
-
handler: routeObj.handler,
|
|
5704
|
-
public: routeObj.public
|
|
5705
|
-
};
|
|
5706
|
-
}
|
|
5707
|
-
}
|
|
5708
|
-
}
|
|
5709
|
-
break;
|
|
5710
|
-
}
|
|
5711
|
-
} catch {}
|
|
5712
|
-
}
|
|
5713
|
-
if (!resolvedPlugin?.id || !resolvedPlugin?.version) {
|
|
5714
|
-
consola.error("Could not extract plugin definition. Expected one of:\n - createPlugin() export (native format)\n - Descriptor factory function returning { id, version, ... } (standard format)");
|
|
5715
|
-
process.exit(1);
|
|
5716
|
-
}
|
|
5717
|
-
const manifest = extractManifest(resolvedPlugin);
|
|
5718
|
-
if (resolvedPlugin.admin?.entry) {
|
|
5719
|
-
consola.error("Plugin declares adminEntry — React admin components require native/trusted mode. Use Block Kit for sandboxed admin pages, or remove adminEntry.");
|
|
5720
|
-
process.exit(1);
|
|
5721
|
-
}
|
|
5722
|
-
if (resolvedPlugin.admin?.portableTextBlocks && resolvedPlugin.admin.portableTextBlocks.length > 0) {
|
|
5723
|
-
consola.error("Plugin declares portableTextBlocks — these require native/trusted mode and cannot be bundled for the marketplace.");
|
|
5724
|
-
process.exit(1);
|
|
5725
|
-
}
|
|
5726
|
-
consola.success(`Plugin: ${manifest.id}@${manifest.version}`);
|
|
5727
|
-
consola.info(` Capabilities: ${manifest.capabilities.length > 0 ? manifest.capabilities.join(", ") : "(none)"}`);
|
|
5728
|
-
consola.info(` Hooks: ${manifest.hooks.length > 0 ? manifest.hooks.map((h) => typeof h === "string" ? h : h.name).join(", ") : "(none)"}`);
|
|
5729
|
-
consola.info(` Routes: ${manifest.routes.length > 0 ? manifest.routes.map((r) => typeof r === "string" ? r : r.name).join(", ") : "(none)"}`);
|
|
5730
|
-
const bundleDir = join(tmpDir, "bundle");
|
|
5731
|
-
await mkdir(bundleDir, { recursive: true });
|
|
5732
|
-
if (backendEntry) {
|
|
5733
|
-
consola.start("Bundling backend...");
|
|
5734
|
-
const shimDir = join(tmpDir, "shims");
|
|
5735
|
-
await mkdir(shimDir, { recursive: true });
|
|
5736
|
-
await writeFile(join(shimDir, "dineway.mjs"), "export const definePlugin = (d) => d;\n");
|
|
5737
|
-
await build({
|
|
5738
|
-
config: false,
|
|
5739
|
-
entry: [backendEntry],
|
|
5740
|
-
format: "esm",
|
|
5741
|
-
outDir: join(tmpDir, "backend"),
|
|
5742
|
-
dts: false,
|
|
5743
|
-
platform: "neutral",
|
|
5744
|
-
external: [],
|
|
5745
|
-
alias: { dineway: join(shimDir, "dineway.mjs") },
|
|
5746
|
-
minify: true,
|
|
5747
|
-
treeshake: true
|
|
5748
|
-
});
|
|
5749
|
-
const backendBaseName = basename(backendEntry).replace(TS_EXT_RE, "");
|
|
5750
|
-
const backendOutputPath = await findBuildOutput(join(tmpDir, "backend"), backendBaseName);
|
|
5751
|
-
if (backendOutputPath) {
|
|
5752
|
-
await copyFile(backendOutputPath, join(bundleDir, "backend.js"));
|
|
5753
|
-
consola.success("Built backend.js");
|
|
5754
|
-
} else {
|
|
5755
|
-
consola.error("Backend build produced no output");
|
|
5756
|
-
process.exit(1);
|
|
5757
|
-
}
|
|
5758
|
-
} else {
|
|
5759
|
-
consola.warn("No sandbox entry found — bundle will have no backend.js");
|
|
5760
|
-
consola.warn(" Add a \"sandbox-entry.ts\" in src/ or a \"./sandbox\" export in package.json");
|
|
5761
|
-
}
|
|
5762
|
-
if (adminEntry) {
|
|
5763
|
-
consola.start("Bundling admin...");
|
|
5764
|
-
await build({
|
|
5765
|
-
config: false,
|
|
5766
|
-
entry: [adminEntry],
|
|
5767
|
-
format: "esm",
|
|
5768
|
-
outDir: join(tmpDir, "admin"),
|
|
5769
|
-
dts: false,
|
|
5770
|
-
platform: "neutral",
|
|
5771
|
-
external: [],
|
|
5772
|
-
minify: true,
|
|
5773
|
-
treeshake: true
|
|
5774
|
-
});
|
|
5775
|
-
const adminBaseName = basename(adminEntry).replace(TS_EXT_RE, "");
|
|
5776
|
-
const adminOutputPath = await findBuildOutput(join(tmpDir, "admin"), adminBaseName);
|
|
5777
|
-
if (adminOutputPath) {
|
|
5778
|
-
await copyFile(adminOutputPath, join(bundleDir, "admin.js"));
|
|
5779
|
-
consola.success("Built admin.js");
|
|
5780
|
-
}
|
|
5781
|
-
}
|
|
5782
|
-
await writeFile(join(bundleDir, "manifest.json"), JSON.stringify(manifest, null, 2));
|
|
5783
|
-
consola.start("Collecting assets...");
|
|
5784
|
-
const readmePath = join(pluginDir, "README.md");
|
|
5785
|
-
if (await fileExists$1(readmePath)) {
|
|
5786
|
-
await copyFile(readmePath, join(bundleDir, "README.md"));
|
|
5787
|
-
consola.success("Included README.md");
|
|
5788
|
-
}
|
|
5789
|
-
const iconPath = join(pluginDir, "icon.png");
|
|
5790
|
-
if (await fileExists$1(iconPath)) {
|
|
5791
|
-
const dims = readImageDimensions(await readFile(iconPath));
|
|
5792
|
-
if (!dims) consola.warn("icon.png is not a valid PNG — skipping");
|
|
5793
|
-
else if (dims[0] !== ICON_SIZE || dims[1] !== ICON_SIZE) {
|
|
5794
|
-
consola.warn(`icon.png is ${dims[0]}x${dims[1]}, expected ${ICON_SIZE}x${ICON_SIZE} — including anyway`);
|
|
5795
|
-
await copyFile(iconPath, join(bundleDir, "icon.png"));
|
|
5796
|
-
} else {
|
|
5797
|
-
await copyFile(iconPath, join(bundleDir, "icon.png"));
|
|
5798
|
-
consola.success("Included icon.png");
|
|
5799
|
-
}
|
|
5800
|
-
}
|
|
5801
|
-
const screenshotsDir = join(pluginDir, "screenshots");
|
|
5802
|
-
if (await fileExists$1(screenshotsDir)) {
|
|
5803
|
-
const screenshotFiles = (await readdir(screenshotsDir)).filter((f) => {
|
|
5804
|
-
const ext = extname(f).toLowerCase();
|
|
5805
|
-
return ext === ".png" || ext === ".jpg" || ext === ".jpeg";
|
|
5806
|
-
}).toSorted().slice(0, MAX_SCREENSHOTS);
|
|
5807
|
-
if (screenshotFiles.length > 0) {
|
|
5808
|
-
await mkdir(join(bundleDir, "screenshots"), { recursive: true });
|
|
5809
|
-
for (const file of screenshotFiles) {
|
|
5810
|
-
const filePath = join(screenshotsDir, file);
|
|
5811
|
-
const dims = readImageDimensions(await readFile(filePath));
|
|
5812
|
-
if (!dims) {
|
|
5813
|
-
consola.warn(`screenshots/${file} — cannot read dimensions, skipping`);
|
|
5814
|
-
continue;
|
|
5815
|
-
}
|
|
5816
|
-
if (dims[0] > MAX_SCREENSHOT_WIDTH || dims[1] > MAX_SCREENSHOT_HEIGHT) consola.warn(`screenshots/${file} is ${dims[0]}x${dims[1]}, max ${MAX_SCREENSHOT_WIDTH}x${MAX_SCREENSHOT_HEIGHT} — including anyway`);
|
|
5817
|
-
await copyFile(filePath, join(bundleDir, "screenshots", file));
|
|
5818
|
-
}
|
|
5819
|
-
consola.success(`Included ${screenshotFiles.length} screenshot(s)`);
|
|
5820
|
-
}
|
|
5821
|
-
}
|
|
5822
|
-
consola.start("Validating bundle...");
|
|
5823
|
-
let hasErrors = false;
|
|
5824
|
-
if (pkg.exports) for (const issue of findSourceExports(pkg.exports)) {
|
|
5825
|
-
consola.error(`Export "${issue.exportPath}" points to source (${issue.resolvedPath}). Package exports must point to built files (e.g. dist/*.mjs). Add a build step and update the exports map.`);
|
|
5826
|
-
hasErrors = true;
|
|
5827
|
-
}
|
|
5828
|
-
const backendPath = join(bundleDir, "backend.js");
|
|
5829
|
-
if (await fileExists$1(backendPath)) {
|
|
5830
|
-
const builtins = findNodeBuiltinImports(await readFile(backendPath, "utf-8"));
|
|
5831
|
-
if (builtins.length > 0) {
|
|
5832
|
-
consola.error(`backend.js imports Node.js built-in modules: ${builtins.join(", ")}`);
|
|
5833
|
-
consola.error("Sandboxed plugins cannot use Node.js APIs");
|
|
5834
|
-
hasErrors = true;
|
|
5835
|
-
}
|
|
5836
|
-
}
|
|
5837
|
-
if (manifest.capabilities.includes("network:request:unrestricted")) consola.warn("Plugin declares unrestricted network access (network:request:unrestricted) — it can make requests to any host");
|
|
5838
|
-
else if (manifest.capabilities.includes("network:request") && manifest.allowedHosts.length === 0) consola.warn("Plugin declares network:request capability but no allowedHosts — all fetch requests will be blocked");
|
|
5839
|
-
if (resolvedPlugin.admin?.portableTextBlocks && resolvedPlugin.admin.portableTextBlocks.length > 0) consola.warn("Plugin declares portableTextBlocks — these require trusted mode and will be ignored in sandboxed plugins");
|
|
5840
|
-
if (resolvedPlugin.admin?.entry) consola.warn("Plugin declares admin.entry — custom React components require trusted mode. Use Block Kit for sandboxed admin pages");
|
|
5841
|
-
if (resolvedPlugin.hooks["page:fragments"]) consola.warn("Plugin declares page:fragments hook — this is trusted-only and will not work in sandboxed mode");
|
|
5842
|
-
const hasAdminPages = (manifest.admin?.pages?.length ?? 0) > 0;
|
|
5843
|
-
const hasAdminWidgets = (manifest.admin?.widgets?.length ?? 0) > 0;
|
|
5844
|
-
if (hasAdminPages || hasAdminWidgets) {
|
|
5845
|
-
if (!manifest.routes.map((r) => typeof r === "string" ? r : r.name).includes("admin")) {
|
|
5846
|
-
consola.error(`Plugin declares ${hasAdminPages ? "adminPages" : ""}${hasAdminPages && hasAdminWidgets ? " and " : ""}${hasAdminWidgets ? "adminWidgets" : ""} but the sandbox entry has no "admin" route. Add an admin route handler to serve Block Kit pages.`);
|
|
5847
|
-
hasErrors = true;
|
|
5848
|
-
}
|
|
5849
|
-
}
|
|
5850
|
-
const bundleEntries = await collectBundleEntries(bundleDir);
|
|
5851
|
-
const sizeViolations = validateBundleSize(bundleEntries);
|
|
5852
|
-
if (sizeViolations.length > 0) {
|
|
5853
|
-
for (const violation of sizeViolations) consola.error(violation);
|
|
5854
|
-
hasErrors = true;
|
|
5855
|
-
} else consola.info(`Bundle size: ${formatBytes(totalBundleBytes(bundleEntries))} across ${bundleEntries.length} file${bundleEntries.length === 1 ? "" : "s"}`);
|
|
5856
|
-
if (hasErrors) {
|
|
5857
|
-
consola.error("Bundle validation failed");
|
|
5858
|
-
process.exit(1);
|
|
5859
|
-
}
|
|
5860
|
-
consola.success("Validation passed");
|
|
5861
|
-
if (validateOnly) return;
|
|
5862
|
-
await mkdir(outDir, { recursive: true });
|
|
5863
|
-
const tarballName = `${manifest.id.replace(SLASH_RE, "-").replace(LEADING_AT_RE, "")}-${manifest.version}.tar.gz`;
|
|
5864
|
-
const tarballPath = join(outDir, tarballName);
|
|
5865
|
-
consola.start("Creating tarball...");
|
|
5866
|
-
await createTarball(bundleDir, tarballPath);
|
|
5867
|
-
const tarballSizeKB = ((await stat(tarballPath)).size / 1024).toFixed(1);
|
|
5868
|
-
const tarballBuf = await readFile(tarballPath);
|
|
5869
|
-
const checksum = createHash("sha256").update(tarballBuf).digest("hex");
|
|
5870
|
-
consola.success(`Created ${tarballName} (${tarballSizeKB}KB)`);
|
|
5871
|
-
consola.info(` SHA-256: ${checksum}`);
|
|
5872
|
-
consola.info(` Path: ${tarballPath}`);
|
|
5873
|
-
} finally {
|
|
5874
|
-
if (tmpDir.endsWith(".dineway-bundle-tmp")) await rm(tmpDir, {
|
|
5875
|
-
recursive: true,
|
|
5876
|
-
force: true
|
|
5877
|
-
});
|
|
5878
|
-
}
|
|
5879
|
-
}
|
|
5880
|
-
});
|
|
5881
|
-
|
|
5882
5399
|
//#endregion
|
|
5883
5400
|
//#region src/cli/commands/plugin-init.ts
|
|
5884
5401
|
/**
|
|
@@ -5954,7 +5471,7 @@ const pluginInitCommand = defineCommand({
|
|
|
5954
5471
|
process.exit(1);
|
|
5955
5472
|
}
|
|
5956
5473
|
const srcDir = join(targetDir, "src");
|
|
5957
|
-
if (await fileExists$
|
|
5474
|
+
if (await fileExists$11(join(targetDir, "package.json"))) {
|
|
5958
5475
|
consola.error(`package.json already exists in ${targetDir}`);
|
|
5959
5476
|
process.exit(1);
|
|
5960
5477
|
}
|
|
@@ -6320,6 +5837,7 @@ async function pollGitHubDeviceFlow(tokenEndpoint, clientId, deviceCode, interva
|
|
|
6320
5837
|
const manifestSummarySchema = pluginManifestSchema.pick({
|
|
6321
5838
|
id: true,
|
|
6322
5839
|
version: true,
|
|
5840
|
+
declaredAccess: true,
|
|
6323
5841
|
capabilities: true,
|
|
6324
5842
|
allowedHosts: true
|
|
6325
5843
|
});
|
|
@@ -6337,7 +5855,20 @@ async function readManifestFromTarball(tarballPath) {
|
|
|
6337
5855
|
const parsed = JSON.parse(content);
|
|
6338
5856
|
const result = manifestSummarySchema.safeParse(parsed);
|
|
6339
5857
|
if (!result.success) throw new Error(`Invalid manifest.json: ${result.error.message}`);
|
|
6340
|
-
|
|
5858
|
+
const reconciled = reconcileManifestAccess({
|
|
5859
|
+
...result.data,
|
|
5860
|
+
storage: {},
|
|
5861
|
+
hooks: [],
|
|
5862
|
+
routes: [],
|
|
5863
|
+
admin: {}
|
|
5864
|
+
});
|
|
5865
|
+
return {
|
|
5866
|
+
id: reconciled.id,
|
|
5867
|
+
version: reconciled.version,
|
|
5868
|
+
declaredAccess: reconciled.declaredAccess,
|
|
5869
|
+
capabilities: reconciled.capabilities,
|
|
5870
|
+
allowedHosts: reconciled.allowedHosts
|
|
5871
|
+
};
|
|
6341
5872
|
}
|
|
6342
5873
|
const POLL_INTERVAL_MS = 3e3;
|
|
6343
5874
|
const POLL_TIMEOUT_MS = 12e4;
|
|
@@ -6422,7 +5953,7 @@ const publishCommand = defineCommand({
|
|
|
6422
5953
|
const pluginDir = resolve(args.dir);
|
|
6423
5954
|
try {
|
|
6424
5955
|
const { runCommand } = await import("citty");
|
|
6425
|
-
const { bundleCommand } = await
|
|
5956
|
+
const { bundleCommand } = await import("../bundle-7C_DpkEq.mjs").then((n) => n.n);
|
|
6426
5957
|
await runCommand(bundleCommand, { rawArgs: ["--dir", pluginDir] });
|
|
6427
5958
|
} catch {
|
|
6428
5959
|
consola.error("Build failed");
|
|
@@ -7086,7 +6617,7 @@ const seedCommand = defineCommand({
|
|
|
7086
6617
|
},
|
|
7087
6618
|
"no-content": {
|
|
7088
6619
|
type: "boolean",
|
|
7089
|
-
description: "Skip sample content",
|
|
6620
|
+
description: "Skip sample data (content entries, bylines, taxonomy terms)",
|
|
7090
6621
|
default: false
|
|
7091
6622
|
},
|
|
7092
6623
|
"on-conflict": {
|
|
@@ -7304,13 +6835,15 @@ var ForgewaySeoGatewayClient = class {
|
|
|
7304
6835
|
}
|
|
7305
6836
|
async request(path, body, init) {
|
|
7306
6837
|
const credentials = await this.refreshAccessToken(await this.loadCredentials());
|
|
6838
|
+
const method = init?.method ?? "POST";
|
|
7307
6839
|
const response = await this.fetchImpl(`${credentials.platformApiUrl}${path}`, {
|
|
7308
|
-
method
|
|
6840
|
+
method,
|
|
7309
6841
|
headers: {
|
|
7310
6842
|
Authorization: `Bearer ${credentials.accessToken}`,
|
|
7311
|
-
"Content-Type": "application/json"
|
|
6843
|
+
"Content-Type": "application/json",
|
|
6844
|
+
"Idempotency-Key": init?.idempotencyKey ?? randomUUID()
|
|
7312
6845
|
},
|
|
7313
|
-
body:
|
|
6846
|
+
body: method === "GET" ? void 0 : JSON.stringify(body ?? {})
|
|
7314
6847
|
});
|
|
7315
6848
|
const payload = await parseJsonResponse(response);
|
|
7316
6849
|
if (!response.ok) throw new ForgewaySeoApiError(errorMessage(payload, `Forgeway SEO request failed with status ${response.status}.`), response.status, payload);
|
|
@@ -7375,6 +6908,11 @@ async function runSeoRequest(args, path, body) {
|
|
|
7375
6908
|
try {
|
|
7376
6909
|
output(await new ForgewaySeoGatewayClient().request(path, body), args);
|
|
7377
6910
|
} catch (error) {
|
|
6911
|
+
if (await handleCliPaywall({
|
|
6912
|
+
error,
|
|
6913
|
+
resource: path,
|
|
6914
|
+
context: { providerPath: path }
|
|
6915
|
+
})) process.exit(2);
|
|
7378
6916
|
consola$1.error(error instanceof Error ? error.message : "Unknown error");
|
|
7379
6917
|
process.exit(1);
|
|
7380
6918
|
}
|
|
@@ -7517,6 +7055,11 @@ const firecrawlCommand = defineCommand({
|
|
|
7517
7055
|
try {
|
|
7518
7056
|
output(await new ForgewaySeoGatewayClient().request(`/api/firecrawl/crawl/${encodeURIComponent(crawlId)}`, void 0, { method: "GET" }), args);
|
|
7519
7057
|
} catch (error) {
|
|
7058
|
+
if (await handleCliPaywall({
|
|
7059
|
+
error,
|
|
7060
|
+
resource: `/api/firecrawl/crawl/${crawlId}`,
|
|
7061
|
+
context: { providerPath: `/api/firecrawl/crawl/${crawlId}` }
|
|
7062
|
+
})) process.exit(2);
|
|
7520
7063
|
consola$1.error(error instanceof Error ? error.message : "Unknown error");
|
|
7521
7064
|
process.exit(1);
|
|
7522
7065
|
}
|
|
@@ -7972,6 +7515,7 @@ runMain(defineCommand({
|
|
|
7972
7515
|
},
|
|
7973
7516
|
subCommands: {
|
|
7974
7517
|
init: initCommand,
|
|
7518
|
+
account: accountCommand,
|
|
7975
7519
|
types: typesCommand,
|
|
7976
7520
|
dev: devCommand,
|
|
7977
7521
|
deploy: deployCommand,
|