fedipod-server 0.29.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -6
- package/dist/claims.d.ts +2 -0
- package/dist/claims.js +13 -4
- package/dist/door.d.ts +4 -0
- package/dist/door.js +31 -0
- package/dist/fedipod.d.ts +15 -0
- package/dist/fedipod.js +13 -0
- package/dist/front.d.ts +3 -0
- package/dist/front.js +92 -0
- package/dist/handler.d.ts +36 -101
- package/dist/handler.js +93 -644
- package/dist/handler.jsonld +2 -30
- package/dist/identities.d.ts +71 -0
- package/dist/identities.js +236 -0
- package/dist/mounts.d.ts +43 -0
- package/dist/mounts.js +123 -0
- package/dist/opt-in.d.ts +46 -0
- package/dist/opt-in.js +176 -0
- package/dist/private-read.d.ts +4 -0
- package/dist/private-read.js +34 -0
- package/package.json +12 -20
- package/lib/client/c2s.mjs +0 -636
- package/lib/client/localapi.mjs +0 -87
- package/lib/client/masto/accounts.mjs +0 -310
- package/lib/client/masto/body.mjs +0 -156
- package/lib/client/masto/index.mjs +0 -206
- package/lib/client/masto/instance.mjs +0 -112
- package/lib/client/masto/media.mjs +0 -79
- package/lib/client/masto/oauth.mjs +0 -656
- package/lib/client/masto/render.mjs +0 -518
- package/lib/client/masto/statuses.mjs +0 -372
- package/lib/client/masto/timelines.mjs +0 -438
- package/lib/client/oidc-auth.mjs +0 -62
- package/lib/client/streaming.mjs +0 -154
- package/lib/client/webpush.mjs +0 -98
- package/lib/connections/acctfeed.mjs +0 -257
- package/lib/connections/atproto.mjs +0 -309
- package/lib/connections/bskyfeed.mjs +0 -243
- package/lib/connections/bskygroup.mjs +0 -162
- package/lib/connections/fediacct.mjs +0 -253
- package/lib/connections/import.mjs +0 -357
- package/lib/connections/tagfeed.mjs +0 -212
- package/lib/connections/vault.mjs +0 -114
- package/lib/core/as2.mjs +0 -206
- package/lib/core/contexts/activitystreams.json +0 -379
- package/lib/core/contexts/anno.json +0 -126
- package/lib/core/contexts/did-v1.json +0 -57
- package/lib/core/contexts/fep-5711.json +0 -36
- package/lib/core/contexts/gotosocial.json +0 -86
- package/lib/core/contexts/identity-v1.json +0 -152
- package/lib/core/contexts/index.mjs +0 -63
- package/lib/core/contexts/join-lemmy.json +0 -33
- package/lib/core/contexts/joinmastodon.json +0 -28
- package/lib/core/contexts/litepub-0.1.json +0 -60
- package/lib/core/contexts/map.json +0 -19
- package/lib/core/contexts/miscellany.json +0 -19
- package/lib/core/contexts/quotes.json +0 -18
- package/lib/core/contexts/schemaorg.json +0 -8845
- package/lib/core/contexts/security-data-integrity-v1.json +0 -78
- package/lib/core/contexts/security-data-integrity-v2.json +0 -81
- package/lib/core/contexts/security-multikey-v1.json +0 -35
- package/lib/core/contexts/security-v1.json +0 -74
- package/lib/core/contexts/webfinger.json +0 -10
- package/lib/core/deliver.mjs +0 -387
- package/lib/core/graphview.mjs +0 -327
- package/lib/core/intake/activities.mjs +0 -649
- package/lib/core/intake/activity.mjs +0 -318
- package/lib/core/intake/channel.mjs +0 -144
- package/lib/core/intake/group.mjs +0 -258
- package/lib/core/intake/index.mjs +0 -732
- package/lib/core/intake/notes.mjs +0 -367
- package/lib/core/intake/verify.mjs +0 -142
- package/lib/core/keys.mjs +0 -153
- package/lib/core/lease.mjs +0 -206
- package/lib/core/place.mjs +0 -82
- package/lib/core/polls.mjs +0 -105
- package/lib/core/profile-page.mjs +0 -104
- package/lib/core/proof.mjs +0 -79
- package/lib/core/publisher/collections.mjs +0 -272
- package/lib/core/publisher/index.mjs +0 -522
- package/lib/core/publisher/notes.mjs +0 -519
- package/lib/core/publisher/own.mjs +0 -143
- package/lib/core/publisher/questions.mjs +0 -241
- package/lib/core/publisher/restore.mjs +0 -224
- package/lib/core/shapes/activitystreams.ttl +0 -157
- package/lib/core/shapes/index.mjs +0 -132
- package/lib/core/shapes/shapes-text.mjs +0 -13
- package/lib/core/social.mjs +0 -561
- package/lib/core/storage.mjs +0 -176
- package/lib/core/store.mjs +0 -578
- package/lib/core/wire-quotes.mjs +0 -71
- package/lib/core/wire.mjs +0 -925
- package/lib/device/account.mjs +0 -87
- package/lib/device/admin/index.mjs +0 -13
- package/lib/device/admin/origins.mjs +0 -35
- package/lib/device/admin/routes/connections.mjs +0 -144
- package/lib/device/admin/routes/gateway.mjs +0 -199
- package/lib/device/admin/routes/lifecycle.mjs +0 -200
- package/lib/device/admin/routes/owner.mjs +0 -322
- package/lib/device/admin/routes/setup.mjs +0 -428
- package/lib/device/admin/routes/social.mjs +0 -188
- package/lib/device/admin/server.mjs +0 -95
- package/lib/device/admin/static.mjs +0 -247
- package/lib/device/admin/surface.mjs +0 -315
- package/lib/device/certs.mjs +0 -281
- package/lib/device/cli/commands/account.mjs +0 -586
- package/lib/device/cli/commands/run.mjs +0 -278
- package/lib/device/cli/commands/service.mjs +0 -221
- package/lib/device/cli/commands/setup.mjs +0 -489
- package/lib/device/cli/commands/state.mjs +0 -561
- package/lib/device/cli/context.mjs +0 -288
- package/lib/device/export-collections.mjs +0 -161
- package/lib/device/gateway-move.mjs +0 -58
- package/lib/device/home.mjs +0 -161
- package/lib/device/migrate.mjs +0 -115
- package/lib/device/ports.mjs +0 -27
- package/lib/device/remote.mjs +0 -54
- package/lib/device/setup.mjs +0 -496
- package/lib/device/update.mjs +0 -96
- package/lib/gateway/caches.mjs +0 -36
- package/lib/gateway/directory.mjs +0 -90
- package/lib/gateway/front-core.mjs +0 -966
- package/lib/gateway/gateway-core.mjs +0 -244
- package/lib/gateway/headers.mjs +0 -49
- package/lib/gateway/httpsig.mjs +0 -159
- package/lib/gateway/notices.mjs +0 -84
- package/lib/gateway/quiet.mjs +0 -189
- package/lib/gateway/relay-extras.mjs +0 -89
- package/lib/gateway/token-claims.mjs +0 -16
- package/lib/pod/README.md +0 -61
- package/lib/pod/actor.mjs +0 -96
- package/lib/pod/collection.mjs +0 -77
- package/lib/pod/containers.mjs +0 -140
- package/lib/pod/discovery.mjs +0 -46
- package/lib/pod/featured.mjs +0 -18
- package/lib/pod/followers.mjs +0 -24
- package/lib/pod/following.mjs +0 -10
- package/lib/pod/http.mjs +0 -74
- package/lib/pod/inbox.mjs +0 -166
- package/lib/pod/links.mjs +0 -35
- package/lib/pod/location.mjs +0 -52
- package/lib/pod/media.mjs +0 -27
- package/lib/pod/notes.mjs +0 -78
- package/lib/pod/notifications.mjs +0 -89
- package/lib/pod/outbox.mjs +0 -18
- package/lib/pod/package.json +0 -19
- package/lib/pod/policy.mjs +0 -58
- package/lib/pod/private.mjs +0 -19
- package/lib/pod/root.mjs +0 -104
- package/lib/pod/state.mjs +0 -40
- package/lib/pod/transport.mjs +0 -674
- package/lib/pod/type-index.mjs +0 -101
- package/lib/pod/urls.mjs +0 -91
- package/lib/server/embed.mjs +0 -424
- package/lib/session/README.md +0 -137
- package/lib/session/demo.html +0 -94
- package/lib/session/fedi-account.mjs +0 -417
- package/lib/session/fedi-login.mjs +0 -123
- package/lib/session/oidc-session.mjs +0 -233
- package/lib/session/package.json +0 -49
- package/lib/shared/guard.mjs +0 -213
- package/lib/shared/links.mjs +0 -9
- package/lib/shared/safefetch.mjs +0 -235
- package/lib/shared/ua.mjs +0 -29
- package/phanpy/dist/404.html +0 -40
- package/phanpy/dist/_headers +0 -2
- package/phanpy/dist/apple-touch-icon.png +0 -0
- package/phanpy/dist/assets/ICONS-DIPJeU0C.js +0 -2
- package/phanpy/dist/assets/ICONS-DIPJeU0C.js.map +0 -1
- package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js +0 -183
- package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js.map +0 -1
- package/phanpy/dist/assets/Temml-CCjoacWt.woff2 +0 -0
- package/phanpy/dist/assets/__vite-browser-external-C7iut881.js +0 -2
- package/phanpy/dist/assets/__vite-browser-external-C7iut881.js.map +0 -1
- package/phanpy/dist/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
- package/phanpy/dist/assets/catch-up-O5q5C75Z.png +0 -0
- package/phanpy/dist/assets/chunk-aKtaBQYM.js +0 -1
- package/phanpy/dist/assets/compose-CFEx7wb4.js +0 -2
- package/phanpy/dist/assets/compose-CFEx7wb4.js.map +0 -1
- package/phanpy/dist/assets/compose-ZJN5xSb4.js +0 -35
- package/phanpy/dist/assets/compose-ZJN5xSb4.js.map +0 -1
- package/phanpy/dist/assets/debug.module-YFQgwnIr.js +0 -52
- package/phanpy/dist/assets/debug.module-YFQgwnIr.js.map +0 -1
- package/phanpy/dist/assets/dom-p1YcJ5Rw.js +0 -2
- package/phanpy/dist/assets/dom-p1YcJ5Rw.js.map +0 -1
- package/phanpy/dist/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
- package/phanpy/dist/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
- package/phanpy/dist/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
- package/phanpy/dist/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
- package/phanpy/dist/assets/icons/add-circle-line-WD0mwORD.js +0 -1
- package/phanpy/dist/assets/icons/alert-line-CpFUMf9s.js +0 -1
- package/phanpy/dist/assets/icons/android-2-line-Bzxnrd3S.js +0 -1
- package/phanpy/dist/assets/icons/announcement-line-5nwaJFJm.js +0 -1
- package/phanpy/dist/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +0 -1
- package/phanpy/dist/assets/icons/arrow-down-line-B8uzXzq6.js +0 -1
- package/phanpy/dist/assets/icons/arrow-left-line-CGrGT9C-.js +0 -1
- package/phanpy/dist/assets/icons/arrow-right-line-CE-HERfX.js +0 -1
- package/phanpy/dist/assets/icons/arrow-to-up-line-CvdQjmcy.js +0 -1
- package/phanpy/dist/assets/icons/arrow-up-circle-line-DEparSNF.js +0 -1
- package/phanpy/dist/assets/icons/arrow-up-line-C7yBeugc.js +0 -1
- package/phanpy/dist/assets/icons/arrows-right-line-BtbXmJGP.js +0 -1
- package/phanpy/dist/assets/icons/at-line-CF66Bznk.js +0 -1
- package/phanpy/dist/assets/icons/attachment-line-BY8It-l9.js +0 -1
- package/phanpy/dist/assets/icons/blockquote-line-BS6pU_hm.js +0 -1
- package/phanpy/dist/assets/icons/board-line-C0UWXyA6.js +0 -1
- package/phanpy/dist/assets/icons/bookmark-line-1CtX5JLF.js +0 -1
- package/phanpy/dist/assets/icons/building-5-line-Dd59MzbO.js +0 -1
- package/phanpy/dist/assets/icons/bus-2-line-6_dvA0GG.js +0 -1
- package/phanpy/dist/assets/icons/calendar-day-line-Crpl_fmu.js +0 -1
- package/phanpy/dist/assets/icons/calendar-month-line-vommts2C.js +0 -1
- package/phanpy/dist/assets/icons/calendar-time-add-line-BU_DTlQk.js +0 -1
- package/phanpy/dist/assets/icons/camera-line-Cv69MEei.js +0 -1
- package/phanpy/dist/assets/icons/celebrate-line-B8DvIoi0.js +0 -1
- package/phanpy/dist/assets/icons/chart-bar-line-FK-QOmtF.js +0 -1
- package/phanpy/dist/assets/icons/chart-line-line-BGcJAFWw.js +0 -1
- package/phanpy/dist/assets/icons/chat-3-line-YFK2-hwz.js +0 -1
- package/phanpy/dist/assets/icons/check-circle-line-DsAxFr5-.js +0 -1
- package/phanpy/dist/assets/icons/clipboard-line-BBp364gJ.js +0 -1
- package/phanpy/dist/assets/icons/close-circle-line-CICdiuGz.js +0 -1
- package/phanpy/dist/assets/icons/close-line-DYrFWcjv.js +0 -1
- package/phanpy/dist/assets/icons/cloud-line-tiQAbah8.js +0 -1
- package/phanpy/dist/assets/icons/code-line-fAl8iiV3.js +0 -1
- package/phanpy/dist/assets/icons/comment-2-line-CR3g7WeB.js +0 -1
- package/phanpy/dist/assets/icons/copy-2-line-CLW69yF5.js +0 -1
- package/phanpy/dist/assets/icons/delete-2-line-B8xmbr4n.js +0 -1
- package/phanpy/dist/assets/icons/document-3-line-DBGNhAg2.js +0 -1
- package/phanpy/dist/assets/icons/document-line-C7mb9sb7.js +0 -1
- package/phanpy/dist/assets/icons/down-line-BGqSSThd.js +0 -1
- package/phanpy/dist/assets/icons/edit-4-line-DcgGaEhx.js +0 -1
- package/phanpy/dist/assets/icons/emoji-2-line-D8YgnM-y.js +0 -1
- package/phanpy/dist/assets/icons/exit-line-Dc07BS-x.js +0 -1
- package/phanpy/dist/assets/icons/external-link-line-BvpSkmjO.js +0 -1
- package/phanpy/dist/assets/icons/eye-2-line-CiWXx-f_.js +0 -1
- package/phanpy/dist/assets/icons/eye-close-line-BgTaSJ4P.js +0 -1
- package/phanpy/dist/assets/icons/filter-2-line-B_mR2usy.js +0 -1
- package/phanpy/dist/assets/icons/filter-line-C_nm-3cU.js +0 -1
- package/phanpy/dist/assets/icons/flag-1-line-C2HB05I0.js +0 -1
- package/phanpy/dist/assets/icons/forbid-circle-line-D3LTT9yd.js +0 -1
- package/phanpy/dist/assets/icons/formula-line-DLrt2d6j.js +0 -1
- package/phanpy/dist/assets/icons/grid-line-CgUmEikf.js +0 -1
- package/phanpy/dist/assets/icons/group-line-C5fnX0wU.js +0 -1
- package/phanpy/dist/assets/icons/hand-finger-2-line-BGgxbuL9.js +0 -1
- package/phanpy/dist/assets/icons/hashtag-line-CyTEEXBb.js +0 -1
- package/phanpy/dist/assets/icons/heart-crack-line-BZddbO2C.js +0 -1
- package/phanpy/dist/assets/icons/heart-line-CJ7K47J4.js +0 -1
- package/phanpy/dist/assets/icons/history-2-line-kWWvFzqv.js +0 -1
- package/phanpy/dist/assets/icons/history-line-bGQiwbTH.js +0 -1
- package/phanpy/dist/assets/icons/home-3-line-fRU1zeLG.js +0 -1
- package/phanpy/dist/assets/icons/information-line-iNk6-sOY.js +0 -1
- package/phanpy/dist/assets/icons/keyboard-line-DsCyuQNz.js +0 -1
- package/phanpy/dist/assets/icons/layout-4-line-CZkOX52Z.js +0 -1
- package/phanpy/dist/assets/icons/layout-5-line-B_znt5wO.js +0 -1
- package/phanpy/dist/assets/icons/left-line-eUw0tya4.js +0 -1
- package/phanpy/dist/assets/icons/lightning-line-DaKoDdix.js +0 -1
- package/phanpy/dist/assets/icons/link-2-line-DCqLmMA7.js +0 -1
- package/phanpy/dist/assets/icons/list-check-line-BAxosBh9.js +0 -1
- package/phanpy/dist/assets/icons/lock-line-CPoaXeUW.js +0 -1
- package/phanpy/dist/assets/icons/mail-line-B0P8aa1L.js +0 -1
- package/phanpy/dist/assets/icons/moon-line-CEqNAToP.js +0 -1
- package/phanpy/dist/assets/icons/more-1-fill-Dw2e5xab.js +0 -1
- package/phanpy/dist/assets/icons/more-3-line-BIiVjYTz.js +0 -1
- package/phanpy/dist/assets/icons/notification-line-BZZq2Gtu.js +0 -1
- package/phanpy/dist/assets/icons/pencil-line-CKh8-A1v.js +0 -1
- package/phanpy/dist/assets/icons/photo-album-line-Bnfg2u40.js +0 -1
- package/phanpy/dist/assets/icons/pin-line-DpTolyhs.js +0 -1
- package/phanpy/dist/assets/icons/play-fill-B0-G_8lX.js +0 -1
- package/phanpy/dist/assets/icons/qrcode-2-line-B6SPCaHn.js +0 -1
- package/phanpy/dist/assets/icons/quill-pen-line-B6LUrnCP.js +0 -1
- package/phanpy/dist/assets/icons/quote-left-fill-hQZ1cj_6.js +0 -1
- package/phanpy/dist/assets/icons/radar-line-CKyroTth.js +0 -1
- package/phanpy/dist/assets/icons/react-line-CoIKhhtK.js +0 -1
- package/phanpy/dist/assets/icons/refresh-2-line-D0mTF97S.js +0 -1
- package/phanpy/dist/assets/icons/right-line-B3KWJaqc.js +0 -1
- package/phanpy/dist/assets/icons/rocket-line-4KZl537D.js +0 -1
- package/phanpy/dist/assets/icons/round-fill-C69NTrZ0.js +0 -1
- package/phanpy/dist/assets/icons/round-line-B9k_1uo-.js +0 -1
- package/phanpy/dist/assets/icons/route-line-B28ReHcE.js +0 -1
- package/phanpy/dist/assets/icons/rows-4-line-CrBniagM.js +0 -1
- package/phanpy/dist/assets/icons/scan-line-DSf74YdP.js +0 -1
- package/phanpy/dist/assets/icons/search-2-line-B6geIU1z.js +0 -1
- package/phanpy/dist/assets/icons/settings-3-line-azJwK47w.js +0 -1
- package/phanpy/dist/assets/icons/settings-6-line-C_5zc5YR.js +0 -1
- package/phanpy/dist/assets/icons/share-2-line-D5MxWjrm.js +0 -1
- package/phanpy/dist/assets/icons/share-forward-line-Cp61fZzZ.js +0 -1
- package/phanpy/dist/assets/icons/sparkles-2-line-DYYyXTaW.js +0 -1
- package/phanpy/dist/assets/icons/sparkles-line-C2hY1s6I.js +0 -1
- package/phanpy/dist/assets/icons/time-line-BF3eet1v.js +0 -1
- package/phanpy/dist/assets/icons/transfer-4-line-oisfvTBw.js +0 -1
- package/phanpy/dist/assets/icons/translate-line-BFWol2Ce.js +0 -1
- package/phanpy/dist/assets/icons/unlock-line-X_x5vwv3.js +0 -1
- package/phanpy/dist/assets/icons/upload-3-line-DgzwUJeW.js +0 -1
- package/phanpy/dist/assets/icons/user-4-line-BVSynZCp.js +0 -1
- package/phanpy/dist/assets/icons/user-add-2-line-BlGS5Q8q.js +0 -1
- package/phanpy/dist/assets/icons/user-add-line-BbXdHG4z.js +0 -1
- package/phanpy/dist/assets/icons/user-edit-line-BFAHOV0X.js +0 -1
- package/phanpy/dist/assets/icons/user-follow-line-BXux0wSd.js +0 -1
- package/phanpy/dist/assets/icons/user-star-line-DdYlpHLU.js +0 -1
- package/phanpy/dist/assets/icons/user-warning-line-Cj43j0_E.js +0 -1
- package/phanpy/dist/assets/icons/user-x-line-DIDgNlPH.js +0 -1
- package/phanpy/dist/assets/icons/volume-line-Bs390KQt.js +0 -1
- package/phanpy/dist/assets/icons/volume-mute-line-DzlzIqFZ.js +0 -1
- package/phanpy/dist/assets/icons/walk-line-yNXfB-md.js +0 -1
- package/phanpy/dist/assets/icons/world-2-line-CenkGIwj.js +0 -1
- package/phanpy/dist/assets/icons/zoom-in-line-CaENUphf.js +0 -1
- package/phanpy/dist/assets/icons/zoom-out-line-BDNk0EWo.js +0 -1
- package/phanpy/dist/assets/instances-BNPptnn-.json +0 -333
- package/phanpy/dist/assets/locales/ar-SA-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/ca-ES-DHRW86Tz.js +0 -1
- package/phanpy/dist/assets/locales/cs-CZ-CKoqEuA1.js +0 -1
- package/phanpy/dist/assets/locales/de-DE-ByRPMRFy.js +0 -1
- package/phanpy/dist/assets/locales/eo-UY-CPC7wLCS.js +0 -1
- package/phanpy/dist/assets/locales/es-ES-dJa0Fd9t.js +0 -1
- package/phanpy/dist/assets/locales/eu-ES-C3djlPR2.js +0 -1
- package/phanpy/dist/assets/locales/fa-IR-DTT490Ck.js +0 -1
- package/phanpy/dist/assets/locales/fi-FI-B23MCPuz.js +0 -1
- package/phanpy/dist/assets/locales/fr-FR-BcaQsO5C.js +0 -1
- package/phanpy/dist/assets/locales/gl-ES-BR-CVKBN.js +0 -1
- package/phanpy/dist/assets/locales/he-IL-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/hu-HU-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/it-IT-C1136UFV.js +0 -1
- package/phanpy/dist/assets/locales/ja-JP-CRZmRAhD.js +0 -1
- package/phanpy/dist/assets/locales/kab-Cz8xH_TT.js +0 -1
- package/phanpy/dist/assets/locales/ko-KR-CG9UiY4o.js +0 -1
- package/phanpy/dist/assets/locales/lt-LT-CJcI6sHZ.js +0 -1
- package/phanpy/dist/assets/locales/nb-NO-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/nl-NL-DxN4Yc1t.js +0 -1
- package/phanpy/dist/assets/locales/oc-FR-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/pl-PL-DkwGXf6P.js +0 -1
- package/phanpy/dist/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +0 -1
- package/phanpy/dist/assets/locales/pt-BR-nGL0HdFS.js +0 -1
- package/phanpy/dist/assets/locales/pt-PT-Cp17aj2z.js +0 -1
- package/phanpy/dist/assets/locales/ru-RU-0Y8cIoaK.js +0 -1
- package/phanpy/dist/assets/locales/th-TH-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/tok-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/tr-TR-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/uk-UA-C5E3dFBf.js +0 -1
- package/phanpy/dist/assets/locales/zh-CN-BHJi7_et.js +0 -1
- package/phanpy/dist/assets/locales/zh-TW-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/main-BdqNbG-a.js +0 -26
- package/phanpy/dist/assets/main-BdqNbG-a.js.map +0 -1
- package/phanpy/dist/assets/mock-home-DGhR__He.js +0 -2
- package/phanpy/dist/assets/mock-home-DGhR__He.js.map +0 -1
- package/phanpy/dist/assets/multi-column-ROck0NVt.jpg +0 -0
- package/phanpy/dist/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
- package/phanpy/dist/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
- package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js +0 -2
- package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js.map +0 -1
- package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js +0 -20
- package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js.map +0 -1
- package/phanpy/dist/assets/style-B1iLPTPR.css +0 -2
- package/phanpy/dist/assets/temml-DuyIQMei.js +0 -97
- package/phanpy/dist/assets/temml-DuyIQMei.js.map +0 -1
- package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js +0 -2
- package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js.map +0 -1
- package/phanpy/dist/assets/year-in-posts-B2lywqdx.png +0 -0
- package/phanpy/dist/assets/year-in-posts-DJv_star.js +0 -3
- package/phanpy/dist/assets/year-in-posts-DJv_star.js.map +0 -1
- package/phanpy/dist/compose/index.html +0 -20
- package/phanpy/dist/favicon.ico +0 -0
- package/phanpy/dist/index.html +0 -73
- package/phanpy/dist/logo-192.png +0 -0
- package/phanpy/dist/logo-512.png +0 -0
- package/phanpy/dist/logo-badge-72.png +0 -0
- package/phanpy/dist/logo-maskable-512.png +0 -0
- package/phanpy/dist/logo-monochrome-512.png +0 -0
- package/phanpy/dist/logo-monochrome-maskable-512.png +0 -0
- package/phanpy/dist/manifest.webmanifest +0 -1
- package/phanpy/dist/og-image-2.jpg +0 -0
- package/phanpy/dist/og-image.png +0 -0
- package/phanpy/dist/robots.txt +0 -2
- package/phanpy/dist/sw.js +0 -2
- package/phanpy/dist/sw.js.map +0 -1
- package/phanpy/dist/version.json +0 -1
- package/run-agent.mjs +0 -899
- package/vendor/gate.cjs +0 -138
- package/vendor/idp-grant.cjs +0 -394
- package/vendor/jfetch.cjs +0 -16
- package/web/admin/actors.js +0 -147
- package/web/admin/bar.css +0 -126
- package/web/admin/bar.js +0 -99
- package/web/admin/client/client.js +0 -66
- package/web/admin/client/index.html +0 -69
- package/web/admin/client-pick.js +0 -79
- package/web/admin/common.js +0 -23
- package/web/admin/connections.js +0 -112
- package/web/admin/gateway.js +0 -133
- package/web/admin/group.js +0 -258
- package/web/admin/index.html +0 -612
- package/web/admin/notices-bar.js +0 -90
- package/web/admin/oauth-signin.mjs +0 -37
- package/web/admin/record.js +0 -393
- package/web/admin/setup/index.html +0 -257
- package/web/admin/setup/setup.js +0 -339
- package/web/admin/tokens.css +0 -68
- package/web/admin/upkeep.js +0 -177
- package/web/admin/window.css +0 -55
- package/web/admin/window.js +0 -136
- package/web/app/README.md +0 -81
- package/web/app/admin-facade.mjs +0 -588
- package/web/app/agent.mjs +0 -672
- package/web/app/atproto-browser.mjs +0 -86
- package/web/app/authorize_interaction.html +0 -56
- package/web/app/authorize_interaction.js +0 -69
- package/web/app/boot.mjs +0 -616
- package/web/app/deliver-relay.mjs +0 -129
- package/web/app/dist/boot.js +0 -34562
- package/web/app/dist/boot.js.map +0 -7
- package/web/app/dist/sw.js +0 -73456
- package/web/app/dist/sw.js.map +0 -7
- package/web/app/fediacct-browser.mjs +0 -146
- package/web/app/gateway-move.mjs +0 -60
- package/web/app/idb-kv.mjs +0 -35
- package/web/app/index.html +0 -258
- package/web/app/keys-browser.mjs +0 -71
- package/web/app/keystore.mjs +0 -82
- package/web/app/oidc-session.mjs +0 -6
- package/web/app/pod-auth.mjs +0 -248
- package/web/app/pod-remote.mjs +0 -84
- package/web/app/shims/_globals.mjs +0 -2
- package/web/app/shims/fedify-sig.mjs +0 -77
- package/web/app/shims/node-crypto.mjs +0 -113
- package/web/app/shims/node-fs.mjs +0 -17
- package/web/app/shims/node-path.mjs +0 -19
- package/web/app/shims/node-url.mjs +0 -7
- package/web/app/shims/prelude.js +0 -58
- package/web/app/shims/safefetch.mjs +0 -58
- package/web/app/shims/shapes-text.mjs +0 -8
- package/web/app/shims/web-push.mjs +0 -14
- package/web/app/signup.mjs +0 -357
- package/web/app/sw-src.mjs +0 -220
- package/web/app/update.js +0 -70
- package/web/front/admin.html +0 -55
- package/web/front/admin.js +0 -89
- package/web/front/install.sh +0 -69
- package/web/front/new-account.html +0 -35
- package/web/front/new-account.js +0 -13
- package/web/front/notices.html +0 -69
- package/web/front/notices.js +0 -107
- package/web/front/run.html +0 -64
- package/web/front/run.js +0 -221
- package/web/front/solid-oidc-client.js +0 -6
- package/web/icons/forum.svg +0 -11
- package/web/icons/gateway.svg +0 -14
- package/web/icons/local.svg +0 -11
package/web/app/oidc-session.mjs
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
// oidc-session.mjs — FediPod's Solid-OIDC session: the solid-oidc-session
|
|
2
|
-
// library (lib/session/) bound to this app's database and client name. The
|
|
3
|
-
// page and the service worker both import this file, so both read one session.
|
|
4
|
-
import { solidOidcSession } from '../../lib/session/oidc-session.mjs';
|
|
5
|
-
|
|
6
|
-
export const { beginLogin, completeLogin, getSession, signOut } = solidOidcSession({ dbName: 'fedipod-oidc', clientName: 'FediPod' });
|
package/web/app/pod-auth.mjs
DELETED
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
// pod-auth.mjs — the pod side of sign-in, done in the browser with no server
|
|
2
|
-
// in between. It is the browser-native twin of lib/account.mjs and
|
|
3
|
-
// vendor/idp-grant.cjs: create a Community Solid Server account and pod, mint a
|
|
4
|
-
// client credential, and turn that credential into a DPoP-bound session whose
|
|
5
|
-
// fetch writes to the pod. Everything a Node agent did against a CSS account
|
|
6
|
-
// API, a page does here — proven cross-origin against solidcommunity.net on
|
|
7
|
-
// 2026-09-06 (claude/validation/scn-cross-origin-spike.html).
|
|
8
|
-
//
|
|
9
|
-
// No dependency on lib/: lib is Node (node:crypto, undici, @fedify). This uses
|
|
10
|
-
// only WebCrypto and fetch, so it runs in a tab and in a service worker.
|
|
11
|
-
|
|
12
|
-
const b64u = (buf) => btoa(String.fromCharCode(...new Uint8Array(buf)))
|
|
13
|
-
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
14
|
-
const enc = (o) => b64u(new TextEncoder().encode(JSON.stringify(o)));
|
|
15
|
-
const sha256 = async (str) => crypto.subtle.digest('SHA-256', new TextEncoder().encode(str));
|
|
16
|
-
|
|
17
|
-
// A CSS account-API call: JSON in, JSON out, the account token carried the way
|
|
18
|
-
// CSS wants it (a bearer with its own scheme, not a cookie, in v7).
|
|
19
|
-
async function accountFetch(url, { method = 'GET', body, token } = {}) {
|
|
20
|
-
const headers = { accept: 'application/json' };
|
|
21
|
-
if (body !== undefined) headers['content-type'] = 'application/json';
|
|
22
|
-
if (token) headers.authorization = `CSS-Account-Token ${token}`;
|
|
23
|
-
const res = await fetch(url, { method, headers, body: body === undefined ? undefined : JSON.stringify(body) });
|
|
24
|
-
let json = null;
|
|
25
|
-
try { json = await res.json(); } catch { /* non-JSON error bodies exist */ }
|
|
26
|
-
return { status: res.status, json };
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Is this origin a CSS v7 account API at all? Cheap pre-flight for the form. */
|
|
30
|
-
export async function accountApiControls(issuer) {
|
|
31
|
-
const origin = issuer.replace(/\/+$/, '');
|
|
32
|
-
const { status, json } = await accountFetch(`${origin}/.account/`);
|
|
33
|
-
if (status >= 400 || !json?.controls) return null;
|
|
34
|
-
return json.controls;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Create an account and a pod named `podName`. Idempotent on re-run: an
|
|
39
|
-
* existing account logs in, an existing pod of the same name is reused. Refuses
|
|
40
|
-
* to make a second pod on an account that already has one, which would give it
|
|
41
|
-
* a second WebID a single credential could not match. Mirrors
|
|
42
|
-
* lib/account.mjs's guards exactly, because the failure it prevents (a
|
|
43
|
-
* credential bound to the wrong WebID that 403s every write) is the same here.
|
|
44
|
-
*/
|
|
45
|
-
export async function createAccountWithPod({ issuer, email, password, podName }) {
|
|
46
|
-
const origin = issuer.replace(/\/+$/, '');
|
|
47
|
-
const accountRoot = `${origin}/.account/`;
|
|
48
|
-
|
|
49
|
-
let token;
|
|
50
|
-
const login = await accountFetch(`${accountRoot}login/password/`, { method: 'POST', body: { email, password } });
|
|
51
|
-
if (login.status < 400 && login.json?.authorization) {
|
|
52
|
-
token = login.json.authorization;
|
|
53
|
-
} else {
|
|
54
|
-
const create = await accountFetch(`${accountRoot}account/`, { method: 'POST' });
|
|
55
|
-
if (!create.json?.authorization) {
|
|
56
|
-
throw new Error(`could not create an account at ${origin} (HTTP ${create.status})`
|
|
57
|
-
+ ` — is sign-up open there? ${create.json?.message || ''}`);
|
|
58
|
-
}
|
|
59
|
-
token = create.json.authorization;
|
|
60
|
-
const pwCreate = (await accountFetch(accountRoot, { token })).json?.controls?.password?.create;
|
|
61
|
-
if (!pwCreate) throw new Error('this server is not a CSS v7 account API (no password control)');
|
|
62
|
-
const pw = await accountFetch(pwCreate, { method: 'POST', token, body: { email, password } });
|
|
63
|
-
if (pw.status >= 400) throw new Error(`could not set the password (HTTP ${pw.status}): ${pw.json?.message || ''}`);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const podCreate = (await accountFetch(accountRoot, { token })).json?.controls?.account?.pod;
|
|
67
|
-
if (!podCreate) throw new Error('this server does not offer pod creation through its account API');
|
|
68
|
-
|
|
69
|
-
const findOwn = async () => {
|
|
70
|
-
const pods = (await accountFetch(podCreate, { token })).json?.pods || {};
|
|
71
|
-
return Object.entries(pods).find(([url]) => {
|
|
72
|
-
try {
|
|
73
|
-
const u = new URL(url);
|
|
74
|
-
return u.hostname === podName || u.hostname.startsWith(`${podName}.`)
|
|
75
|
-
|| u.pathname.split('/').filter(Boolean).includes(podName);
|
|
76
|
-
} catch { return false; }
|
|
77
|
-
}) || null;
|
|
78
|
-
};
|
|
79
|
-
const owned = Object.keys((await accountFetch(podCreate, { token })).json?.pods || {});
|
|
80
|
-
if (owned.length && !(await findOwn())) {
|
|
81
|
-
throw new Error(`${email} already has a pod on ${new URL(origin).host} — one account, one pod.`
|
|
82
|
-
+ ' A second identity needs its own account. Nothing was created.');
|
|
83
|
-
}
|
|
84
|
-
const made = await accountFetch(podCreate, { method: 'POST', token, body: { name: podName } });
|
|
85
|
-
let pod = made.json?.pod || made.json?.podBaseUrl || null;
|
|
86
|
-
let webId = made.json?.webId || null;
|
|
87
|
-
if (made.status >= 400 || !pod) {
|
|
88
|
-
const own = await findOwn();
|
|
89
|
-
if (own) { pod = own[0]; webId = webId || own[1]?.webId || null; }
|
|
90
|
-
else if (made.status >= 400) throw new Error(`pod creation failed (HTTP ${made.status}): ${made.json?.message || ''}`);
|
|
91
|
-
}
|
|
92
|
-
if (!pod) throw new Error('the server did not report a pod URL');
|
|
93
|
-
return { pod: pod.endsWith('/') ? pod : pod + '/', webId, accountToken: token };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Mint a client credential against the account. `accountToken` reuses a login
|
|
98
|
-
* from createAccountWithPod; otherwise email+password logs in. Returns the
|
|
99
|
-
* credential record the agent expects, minus remotePod (the caller adds it).
|
|
100
|
-
*/
|
|
101
|
-
export async function mintCredential({ issuer, email, password, webId, podUrl, accountToken, name = 'fedipod' }) {
|
|
102
|
-
const origin = issuer.replace(/\/+$/, '');
|
|
103
|
-
const accountRoot = `${origin}/.account/`;
|
|
104
|
-
|
|
105
|
-
let token = accountToken;
|
|
106
|
-
if (!token) {
|
|
107
|
-
// Sign in with the identifier the form gave (an email). If that account does
|
|
108
|
-
// not own the target pod — or the sign-in fails outright — fall back to the
|
|
109
|
-
// pod's subdomain as a username: older solidcommunity.net accounts sign in by
|
|
110
|
-
// username, not email. Done behind the scenes so the form only asks for an email.
|
|
111
|
-
const tryLogin = async (id) => {
|
|
112
|
-
const r = await accountFetch(`${accountRoot}login/password/`, { method: 'POST', body: { email: id, password } });
|
|
113
|
-
return (r.status < 400 && r.json?.authorization) ? r.json.authorization : null;
|
|
114
|
-
};
|
|
115
|
-
const ownsPod = async (tok) => {
|
|
116
|
-
if (!podUrl) return true;
|
|
117
|
-
try {
|
|
118
|
-
const ctl = (await accountFetch(accountRoot, { token: tok })).json?.controls?.account?.webId;
|
|
119
|
-
const links = ctl ? (await accountFetch(ctl, { token: tok })).json?.webIdLinks : null;
|
|
120
|
-
return Object.keys(links || {}).some((w) => new URL(w).origin === new URL(podUrl).origin);
|
|
121
|
-
} catch { return false; }
|
|
122
|
-
};
|
|
123
|
-
token = await tryLogin(email);
|
|
124
|
-
let sub = null; try { sub = podUrl ? new URL(podUrl).host.split('.')[0] : null; } catch { /* podUrl not a URL */ }
|
|
125
|
-
if (sub && sub !== email && (!token || !(await ownsPod(token)))) {
|
|
126
|
-
const alt = await tryLogin(sub);
|
|
127
|
-
if (alt) token = alt;
|
|
128
|
-
}
|
|
129
|
-
if (!token) throw new Error('account login failed — check the email or username and the password');
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const controls = (await accountFetch(accountRoot, { token })).json?.controls;
|
|
133
|
-
const ccUrl = controls?.account?.clientCredentials;
|
|
134
|
-
if (!ccUrl) throw new Error('this server is not a CSS account API (no clientCredentials control)');
|
|
135
|
-
|
|
136
|
-
let wid = webId;
|
|
137
|
-
if (!wid) {
|
|
138
|
-
const linkCtl = controls?.account?.webId;
|
|
139
|
-
const links = linkCtl ? (await accountFetch(linkCtl, { token })).json?.webIdLinks : null;
|
|
140
|
-
const all = links ? Object.keys(links) : [];
|
|
141
|
-
if (!all.length) throw new Error('no WebID is linked to this account');
|
|
142
|
-
if (podUrl) {
|
|
143
|
-
// The credential MUST be for a WebID that owns the target pod, or every
|
|
144
|
-
// write to it is rejected. Never fall back to an unrelated WebID: a
|
|
145
|
-
// credential bound to the wrong identity 401s/403s on the pod, which is
|
|
146
|
-
// the opaque "could not store the key" people then see.
|
|
147
|
-
let origins = [];
|
|
148
|
-
try { origins = all.filter((w) => new URL(w).origin === new URL(podUrl).origin); } catch { /* podUrl not a URL */ }
|
|
149
|
-
if (!origins.length) {
|
|
150
|
-
throw new Error(`the account you signed in with does not own ${new URL(podUrl).origin}. `
|
|
151
|
-
+ `It is linked to ${all.length} WebID${all.length > 1 ? 's' : ''} — on `
|
|
152
|
-
+ `${all.map((w) => new URL(w).host).join(', ')} — none of them this pod. `
|
|
153
|
-
+ 'Sign in with the account that owns this pod, or let FediPod make you a new one.');
|
|
154
|
-
}
|
|
155
|
-
wid = origins[0];
|
|
156
|
-
} else {
|
|
157
|
-
wid = all[0];
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const made = await accountFetch(ccUrl, { method: 'POST', token, body: { name, webId: wid } });
|
|
162
|
-
if (made.status >= 400 || !made.json?.secret) throw new Error(`mint failed (HTTP ${made.status}): ${made.json?.message || ''}`);
|
|
163
|
-
const tokenEndpoint = await discoverTokenEndpoint(origin);
|
|
164
|
-
return { clientId: made.json.id, secret: made.json.secret, webId: wid, tokenEndpoint,
|
|
165
|
-
resource: made.json.resource, issuerOrigin: origin,
|
|
166
|
-
// Carried so sign-up can hand it straight back to revokeCredential. It is a
|
|
167
|
-
// session token for the ACCOUNT — never stored, never written to the pod,
|
|
168
|
-
// and gone with the tab.
|
|
169
|
-
accountToken: token };
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Delete a client credential.
|
|
174
|
-
*
|
|
175
|
-
* The one sign-up mints is full, permanent access to the pod, and it is needed
|
|
176
|
-
* only while sign-up is running — the agent itself runs on the Solid-OIDC
|
|
177
|
-
* session. Leaving it behind meant every account carried an orphaned key that
|
|
178
|
-
* nothing held and nobody would think to revoke, while the page told the user
|
|
179
|
-
* it was "stored in this browser", which it never was.
|
|
180
|
-
*
|
|
181
|
-
* Best effort by design: a server that will not delete it leaves a credential
|
|
182
|
-
* the owner can still revoke from their pod's own account page, and saying so
|
|
183
|
-
* is better than failing a sign-up that otherwise worked.
|
|
184
|
-
*/
|
|
185
|
-
export async function revokeCredential({ resource, accountToken }) {
|
|
186
|
-
if (!resource || !accountToken) return false;
|
|
187
|
-
try {
|
|
188
|
-
const r = await accountFetch(resource, { method: 'DELETE', token: accountToken });
|
|
189
|
-
return r.status < 400;
|
|
190
|
-
} catch { return false; }
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/** The OIDC token endpoint, from the issuer's discovery document. */
|
|
194
|
-
export async function discoverTokenEndpoint(issuer) {
|
|
195
|
-
const origin = issuer.replace(/\/+$/, '');
|
|
196
|
-
try {
|
|
197
|
-
const res = await fetch(`${origin}/.well-known/openid-configuration`, { headers: { accept: 'application/json' } });
|
|
198
|
-
if (res.ok) {
|
|
199
|
-
const doc = await res.json();
|
|
200
|
-
if (doc.token_endpoint) return doc.token_endpoint;
|
|
201
|
-
}
|
|
202
|
-
} catch { /* fall through to the CSS default */ }
|
|
203
|
-
return `${origin}/.oidc/token`;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* A DPoP-bound session: one ES256 proof key, a client-credentials access token
|
|
208
|
-
* bound to it, and a `fetch` that signs every request. The same key must sign
|
|
209
|
-
* the token request and every resource request, or the token's binding fails,
|
|
210
|
-
* so the key is made once and kept.
|
|
211
|
-
*/
|
|
212
|
-
export async function makeDpopSession({ clientId, secret, tokenEndpoint }) {
|
|
213
|
-
const proofKey = await crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign']);
|
|
214
|
-
const jwk = await crypto.subtle.exportKey('jwk', proofKey.publicKey);
|
|
215
|
-
const publicJwk = { kty: jwk.kty, crv: jwk.crv, x: jwk.x, y: jwk.y };
|
|
216
|
-
|
|
217
|
-
const proof = async (htm, htu, ath) => {
|
|
218
|
-
const header = { typ: 'dpop+jwt', alg: 'ES256', jwk: publicJwk };
|
|
219
|
-
const payload = { htm, htu: htu.split('#')[0], jti: crypto.randomUUID(), iat: Math.floor(Date.now() / 1000), ...(ath ? { ath } : {}) };
|
|
220
|
-
const data = `${enc(header)}.${enc(payload)}`;
|
|
221
|
-
const sig = await crypto.subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, proofKey.privateKey, new TextEncoder().encode(data));
|
|
222
|
-
return `${data}.${b64u(sig)}`;
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
let token = null; let expiresAt = 0;
|
|
226
|
-
const refresh = async () => {
|
|
227
|
-
const basic = btoa(`${encodeURIComponent(clientId)}:${encodeURIComponent(secret)}`);
|
|
228
|
-
const res = await fetch(tokenEndpoint, {
|
|
229
|
-
method: 'POST',
|
|
230
|
-
headers: { authorization: `Basic ${basic}`, 'content-type': 'application/x-www-form-urlencoded', dpop: await proof('POST', tokenEndpoint) },
|
|
231
|
-
body: 'grant_type=client_credentials&scope=webid',
|
|
232
|
-
});
|
|
233
|
-
const tok = await res.json().catch(() => ({}));
|
|
234
|
-
if (!tok.access_token) throw new Error(`token request failed (HTTP ${res.status}): ${tok.error || ''}`);
|
|
235
|
-
token = tok.access_token;
|
|
236
|
-
expiresAt = Date.now() + Math.max(30, (tok.expires_in || 300) - 30) * 1000;
|
|
237
|
-
return token;
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
const authFetch = async (url, init = {}) => {
|
|
241
|
-
if (!token || Date.now() > expiresAt) await refresh();
|
|
242
|
-
const ath = b64u(await sha256(token));
|
|
243
|
-
const headers = { ...(init.headers || {}), authorization: `DPoP ${token}`, dpop: await proof((init.method || 'GET'), url, ath) };
|
|
244
|
-
return fetch(url, { ...init, headers });
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
return { fetch: authFetch, refresh };
|
|
248
|
-
}
|
package/web/app/pod-remote.mjs
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
// pod-remote.mjs — the browser's pod transport, over a DPoP/OIDC session.
|
|
2
|
-
//
|
|
3
|
-
// The conversation with the pod — verbs, ACL documents, the deletion deny-list,
|
|
4
|
-
// the Retry-After cooldown, container listings — is lib/pod/transport.mjs, the
|
|
5
|
-
// same code the Node agent runs. This used to be a hand-kept fork of that file,
|
|
6
|
-
// and the fork had drifted: no cooldown at all for a 429 without a Retry-After
|
|
7
|
-
// header, no byte cap on a listing from a public-Append inbox, a swallowed
|
|
8
|
-
// parse failure, and probes that were neither counted nor held back. Those are
|
|
9
|
-
// gone with the fork.
|
|
10
|
-
//
|
|
11
|
-
// What genuinely differs, and all that is left here: this side retries a
|
|
12
|
-
// dropped connection, and waits a throttle out instead of failing fast.
|
|
13
|
-
|
|
14
|
-
import { PodTransport } from '../../lib/pod/transport.mjs';
|
|
15
|
-
|
|
16
|
-
export { protectedFromDeletion } from '../../lib/pod/transport.mjs';
|
|
17
|
-
|
|
18
|
-
// Retry transient edge throttling (dropped connections, 429/503) with backoff.
|
|
19
|
-
const RETRY_MAX = 5;
|
|
20
|
-
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
21
|
-
const backoff = (n) => Math.min(5000, 400 * 2 ** n) + Math.floor(Math.random() * 250);
|
|
22
|
-
|
|
23
|
-
// A tighter ceiling than the Node side's thirty minutes: a tab is a person
|
|
24
|
-
// waiting, and a hostile or confused header must not park them for half an hour.
|
|
25
|
-
const COOLDOWN_MAX_MS = 5 * 60_000;
|
|
26
|
-
|
|
27
|
-
export class BrowserRemotePod extends PodTransport {
|
|
28
|
-
constructor(session, { webId, log = () => {}, role = 'agent' } = {}) {
|
|
29
|
-
super(session, {
|
|
30
|
-
webId,
|
|
31
|
-
log,
|
|
32
|
-
role,
|
|
33
|
-
runtime: 'browser',
|
|
34
|
-
// Wait the throttle out rather than refusing: there is a person here, and
|
|
35
|
-
// an operation that resumes in four seconds beats one that fails and asks
|
|
36
|
-
// them to try again.
|
|
37
|
-
cooldownMode: 'wait',
|
|
38
|
-
maxCooldownMs: COOLDOWN_MAX_MS,
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async warmup() { /* the DPoP session refreshes lazily */ }
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* solidcommunity.net sits behind an edge (Cloudflare) that throttles a burst
|
|
46
|
-
* of requests: during first-boot provisioning the agent makes ~15 rapid
|
|
47
|
-
* calls, and one comes back as a dropped connection ("Failed to fetch") or a
|
|
48
|
-
* 429/503. A single dropped write used to abort the whole boot. Retry those
|
|
49
|
-
* with escalating backoff — the throttle window clears in under a few seconds.
|
|
50
|
-
* Safe to retry: a thrown request never reached the server, and a 429/503 was
|
|
51
|
-
* refused, not applied.
|
|
52
|
-
*
|
|
53
|
-
* This overrides `_send`, not `fetch`, so it cannot skip the url map, the
|
|
54
|
-
* cooldown accounting or the deletion deny-list — all of which sit in `fetch`
|
|
55
|
-
* above it. The pod-wide pause is armed there too, so a retry here waits for
|
|
56
|
-
* the whole pod rather than each call climbing its own ladder.
|
|
57
|
-
*/
|
|
58
|
-
async _send(url, init) {
|
|
59
|
-
let attempt = 0;
|
|
60
|
-
for (;;) {
|
|
61
|
-
try {
|
|
62
|
-
const res = await this.session.fetch(url, init);
|
|
63
|
-
if ((res.status === 429 || res.status === 503) && attempt < RETRY_MAX) {
|
|
64
|
-
// Arm the pod-wide pause from THIS answer before retrying. The retry
|
|
65
|
-
// happens inside `fetch`, so waiting for the outer `_observe` would
|
|
66
|
-
// mean climbing the whole ladder while ignoring the Retry-After the
|
|
67
|
-
// server just gave us. `_cooldownGate` then sleeps whatever was
|
|
68
|
-
// armed; when the server named no delay, nothing is armed and the
|
|
69
|
-
// backoff below paces it instead.
|
|
70
|
-
this._observe(res);
|
|
71
|
-
if (this.pausedUntil > Date.now()) await this._cooldownGate();
|
|
72
|
-
else await sleep(backoff(attempt));
|
|
73
|
-
attempt++;
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
return res;
|
|
77
|
-
} catch (e) {
|
|
78
|
-
if (attempt >= RETRY_MAX) throw e;
|
|
79
|
-
this.log(`[${this.label}] ${init?.method || 'GET'} ${url} failed (${e.message}); retry ${attempt + 1}/${RETRY_MAX}`);
|
|
80
|
-
await sleep(backoff(attempt++));
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
// fedify-sig.mjs — a browser stand-in for @fedify/fedify/sig.
|
|
2
|
-
//
|
|
3
|
-
// Fedify itself will not run in a browser: it reaches for node:dns, node:net
|
|
4
|
-
// and node:process to resolve and fetch actors. Only two of its functions are
|
|
5
|
-
// on the agent's path:
|
|
6
|
-
//
|
|
7
|
-
// signRequest — outbound HTTP Signatures (draft-cavage), what a remote
|
|
8
|
-
// server verifies. Implemented here with WebCrypto and
|
|
9
|
-
// proven byte-identical to Fedify's own output for the
|
|
10
|
-
// same request and key (claude/validation/signing-shim/).
|
|
11
|
-
// verifyRequestDetailed — inbound verification. The browser agent never runs
|
|
12
|
-
// it: it trusts the gateway's receipt, exactly as the
|
|
13
|
-
// agent does in the gateway's `trust` mode. Stubbed so a
|
|
14
|
-
// bundled import resolves.
|
|
15
|
-
//
|
|
16
|
-
// A browser cannot set Date or Host on a request (both are forbidden header
|
|
17
|
-
// names) and does not add them itself, so a signature that covers them can only
|
|
18
|
-
// travel through the relay, which sends the signed headers verbatim. That is why
|
|
19
|
-
// the real work is `sign()`, which returns the signed headers as data. The
|
|
20
|
-
// browser deliverer hands that to the relay; `signRequest` wraps it into a
|
|
21
|
-
// Request for the Fedify-shaped callers and the byte-equality proof.
|
|
22
|
-
|
|
23
|
-
const b64 = (buf) => btoa(String.fromCharCode(...new Uint8Array(buf)));
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Sign a request and return the parts to send: the covered header names, the
|
|
27
|
-
* header values (Date/Host/Digest filled in), the body, and the target. Host is
|
|
28
|
-
* always signed from the URL; the relay's own fetch sends the matching Host.
|
|
29
|
-
*/
|
|
30
|
-
export async function sign({ url, method = 'POST', headers = {}, body }, privateKey, keyId) {
|
|
31
|
-
const u = new URL(url);
|
|
32
|
-
const m = method.toUpperCase();
|
|
33
|
-
const signed = {};
|
|
34
|
-
for (const [k, v] of Object.entries(headers)) {
|
|
35
|
-
const name = k.toLowerCase();
|
|
36
|
-
if (name !== 'signature' && name !== 'host') signed[name] = v;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
let bodyBytes;
|
|
40
|
-
if (m !== 'GET' && m !== 'HEAD' && body != null) {
|
|
41
|
-
bodyBytes = typeof body === 'string' ? new TextEncoder().encode(body) : new Uint8Array(body);
|
|
42
|
-
if (!('digest' in signed)) signed.digest = `SHA-256=${b64(await crypto.subtle.digest('SHA-256', bodyBytes))}`;
|
|
43
|
-
}
|
|
44
|
-
if (!('date' in signed)) signed.date = new Date().toUTCString();
|
|
45
|
-
signed.host = u.host;
|
|
46
|
-
|
|
47
|
-
const names = ['(request-target)', ...Object.keys(signed).sort()];
|
|
48
|
-
const signingString = names.map((n) => (n === '(request-target)'
|
|
49
|
-
? `(request-target): ${m.toLowerCase()} ${u.pathname}${u.search}`
|
|
50
|
-
: `${n}: ${signed[n]}`)).join('\n');
|
|
51
|
-
const sig = await crypto.subtle.sign('RSASSA-PKCS1-v1_5', privateKey, new TextEncoder().encode(signingString));
|
|
52
|
-
signed.signature = `keyId="${keyId.href || keyId}",algorithm="rsa-sha256",headers="${names.join(' ')}",signature="${b64(sig)}"`;
|
|
53
|
-
|
|
54
|
-
return { url: u.href, method: m, headers: signed, body: bodyBytes ?? null, covered: names };
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** Fedify's signRequest shape: sign, then return a Request. Forbidden headers
|
|
58
|
-
* (Host, Date) survive here only where the runtime allows them (Node); the
|
|
59
|
-
* browser delivery path uses sign() and the relay instead. */
|
|
60
|
-
export async function signRequest(request, privateKey, keyId) {
|
|
61
|
-
const headers = {};
|
|
62
|
-
for (const [k, v] of request.headers) headers[k] = v;
|
|
63
|
-
const body = (request.method === 'GET' || request.method === 'HEAD')
|
|
64
|
-
? undefined : new Uint8Array(await request.clone().arrayBuffer());
|
|
65
|
-
const s = await sign({ url: request.url, method: request.method, headers, body }, privateKey, keyId);
|
|
66
|
-
const out = new Headers();
|
|
67
|
-
for (const [k, v] of Object.entries(s.headers)) out.set(k, v);
|
|
68
|
-
const init = { method: s.method, headers: out, signal: request.signal };
|
|
69
|
-
if (s.body) init.body = s.body;
|
|
70
|
-
return new Request(request.url, init);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// The browser agent verifies nothing itself — the gateway already did, and its
|
|
74
|
-
// receipt is what the drain trusts. Present so the import resolves.
|
|
75
|
-
export async function verifyRequestDetailed() {
|
|
76
|
-
return { success: false, reason: 'the browser agent does not verify; it trusts the gateway receipt' };
|
|
77
|
-
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
// node-crypto.mjs — a browser stand-in for node:crypto, for the agent bundle.
|
|
2
|
-
//
|
|
3
|
-
// The agent uses only a small synchronous slice: SHA-256 (and HMAC-SHA-256)
|
|
4
|
-
// hashing, random bytes, constant-time compare — plus WebCrypto (crypto.subtle),
|
|
5
|
-
// which the browser has natively. Rather than pull in crypto-browserify (which
|
|
6
|
-
// drags Node streams into the bundle), SHA-256 is implemented here in pure JS.
|
|
7
|
-
// Key generation and KeyObject calls are never reached: keys are made by
|
|
8
|
-
// keystore.mjs and loaded straight into WebCrypto.
|
|
9
|
-
|
|
10
|
-
const K = new Uint32Array([
|
|
11
|
-
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
|
|
12
|
-
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
|
|
13
|
-
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
|
|
14
|
-
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
|
|
15
|
-
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
|
|
16
|
-
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
|
|
17
|
-
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
|
|
18
|
-
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2]);
|
|
19
|
-
const rotr = (x, n) => (x >>> n) | (x << (32 - n));
|
|
20
|
-
|
|
21
|
-
function sha256(bytes) {
|
|
22
|
-
const l = bytes.length;
|
|
23
|
-
const withOne = l + 1;
|
|
24
|
-
const k = (56 - (withOne % 64) + 64) % 64;
|
|
25
|
-
const total = withOne + k + 8;
|
|
26
|
-
const m = new Uint8Array(total);
|
|
27
|
-
m.set(bytes); m[l] = 0x80;
|
|
28
|
-
const bits = l * 8;
|
|
29
|
-
const dv = new DataView(m.buffer);
|
|
30
|
-
dv.setUint32(total - 4, bits >>> 0);
|
|
31
|
-
dv.setUint32(total - 8, Math.floor(bits / 0x100000000));
|
|
32
|
-
const H = new Uint32Array([0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19]);
|
|
33
|
-
const w = new Uint32Array(64);
|
|
34
|
-
for (let i = 0; i < total; i += 64) {
|
|
35
|
-
for (let t = 0; t < 16; t++) w[t] = dv.getUint32(i + t * 4);
|
|
36
|
-
for (let t = 16; t < 64; t++) {
|
|
37
|
-
const s0 = rotr(w[t-15],7) ^ rotr(w[t-15],18) ^ (w[t-15] >>> 3);
|
|
38
|
-
const s1 = rotr(w[t-2],17) ^ rotr(w[t-2],19) ^ (w[t-2] >>> 10);
|
|
39
|
-
w[t] = (w[t-16] + s0 + w[t-7] + s1) >>> 0;
|
|
40
|
-
}
|
|
41
|
-
let [a,b,c,d,e,f,g,h] = H;
|
|
42
|
-
for (let t = 0; t < 64; t++) {
|
|
43
|
-
const S1 = rotr(e,6) ^ rotr(e,11) ^ rotr(e,25);
|
|
44
|
-
const ch = (e & f) ^ (~e & g);
|
|
45
|
-
const t1 = (h + S1 + ch + K[t] + w[t]) >>> 0;
|
|
46
|
-
const S0 = rotr(a,2) ^ rotr(a,13) ^ rotr(a,22);
|
|
47
|
-
const maj = (a & b) ^ (a & c) ^ (b & c);
|
|
48
|
-
const t2 = (S0 + maj) >>> 0;
|
|
49
|
-
h=g; g=f; f=e; e=(d+t1)>>>0; d=c; c=b; b=a; a=(t1+t2)>>>0;
|
|
50
|
-
}
|
|
51
|
-
H[0]=(H[0]+a)>>>0; H[1]=(H[1]+b)>>>0; H[2]=(H[2]+c)>>>0; H[3]=(H[3]+d)>>>0;
|
|
52
|
-
H[4]=(H[4]+e)>>>0; H[5]=(H[5]+f)>>>0; H[6]=(H[6]+g)>>>0; H[7]=(H[7]+h)>>>0;
|
|
53
|
-
}
|
|
54
|
-
const out = new Uint8Array(32);
|
|
55
|
-
new DataView(out.buffer).setUint32(0, H[0]); new DataView(out.buffer).setUint32(4, H[1]);
|
|
56
|
-
new DataView(out.buffer).setUint32(8, H[2]); new DataView(out.buffer).setUint32(12, H[3]);
|
|
57
|
-
new DataView(out.buffer).setUint32(16, H[4]); new DataView(out.buffer).setUint32(20, H[5]);
|
|
58
|
-
new DataView(out.buffer).setUint32(24, H[6]); new DataView(out.buffer).setUint32(28, H[7]);
|
|
59
|
-
return out;
|
|
60
|
-
}
|
|
61
|
-
function hmacSha256(key, msg) {
|
|
62
|
-
if (key.length > 64) key = sha256(key);
|
|
63
|
-
const pad = new Uint8Array(64); pad.set(key);
|
|
64
|
-
const ipad = new Uint8Array(64); const opad = new Uint8Array(64);
|
|
65
|
-
for (let i = 0; i < 64; i++) { ipad[i] = pad[i] ^ 0x36; opad[i] = pad[i] ^ 0x5c; }
|
|
66
|
-
const inner = sha256(concat(ipad, msg));
|
|
67
|
-
return sha256(concat(opad, inner));
|
|
68
|
-
}
|
|
69
|
-
const concat = (a, b) => { const o = new Uint8Array(a.length + b.length); o.set(a); o.set(b, a.length); return o; };
|
|
70
|
-
const toBytes = (data, enc) => {
|
|
71
|
-
if (data == null) return new Uint8Array(0);
|
|
72
|
-
if (typeof data === 'string') {
|
|
73
|
-
if (enc === 'hex') return Uint8Array.from(data.match(/.{1,2}/g) || [], (h) => parseInt(h, 16));
|
|
74
|
-
if (enc === 'base64') return Uint8Array.from(atob(data), (c) => c.charCodeAt(0));
|
|
75
|
-
return new TextEncoder().encode(data);
|
|
76
|
-
}
|
|
77
|
-
return data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
78
|
-
};
|
|
79
|
-
const encode = (bytes, enc) => {
|
|
80
|
-
if (!enc || enc === 'buffer') return bytes;
|
|
81
|
-
if (enc === 'hex') return [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
82
|
-
const b64 = btoa(String.fromCharCode(...bytes));
|
|
83
|
-
if (enc === 'base64url') return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
84
|
-
return b64; // base64
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
export function createHash(alg) {
|
|
88
|
-
if (alg !== 'sha256') throw new Error(`node-crypto shim: only sha256 is implemented (got ${alg})`);
|
|
89
|
-
let acc = new Uint8Array(0);
|
|
90
|
-
return { update(d, e) { acc = concat(acc, toBytes(d, e)); return this; }, digest(enc) { return encode(sha256(acc), enc); } };
|
|
91
|
-
}
|
|
92
|
-
export function createHmac(alg, key) {
|
|
93
|
-
if (alg !== 'sha256') throw new Error(`node-crypto shim: only hmac-sha256 is implemented (got ${alg})`);
|
|
94
|
-
const k = toBytes(key); let acc = new Uint8Array(0);
|
|
95
|
-
return { update(d, e) { acc = concat(acc, toBytes(d, e)); return this; }, digest(enc) { return encode(hmacSha256(k, acc), enc); } };
|
|
96
|
-
}
|
|
97
|
-
// A Buffer, not a bare Uint8Array, so callers' `.toString('hex')` works (the
|
|
98
|
-
// prelude defines Buffer before any bundle code runs).
|
|
99
|
-
export function randomBytes(n) { const b = new Uint8Array(n); crypto.getRandomValues(b); return globalThis.Buffer ? globalThis.Buffer.from(b) : b; }
|
|
100
|
-
export const webcrypto = globalThis.crypto;
|
|
101
|
-
export const randomUUID = () => globalThis.crypto.randomUUID();
|
|
102
|
-
export function timingSafeEqual(a, b) {
|
|
103
|
-
if (a.length !== b.length) return false;
|
|
104
|
-
let out = 0; for (let i = 0; i < a.length; i++) out |= a[i] ^ b[i];
|
|
105
|
-
return out === 0;
|
|
106
|
-
}
|
|
107
|
-
const unavailable = (name) => () => { throw new Error(`node:crypto ${name} is not available in the browser agent`); };
|
|
108
|
-
export const generateKeyPairSync = unavailable('generateKeyPairSync');
|
|
109
|
-
export const createPrivateKey = unavailable('createPrivateKey');
|
|
110
|
-
export const createPublicKey = unavailable('createPublicKey');
|
|
111
|
-
export const scryptSync = unavailable('scryptSync');
|
|
112
|
-
export default { createHash, createHmac, randomBytes, webcrypto, randomUUID, timingSafeEqual,
|
|
113
|
-
generateKeyPairSync, createPrivateKey, createPublicKey, scryptSync };
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// node-fs.mjs — the tiny synchronous slice of node:fs touched at import time.
|
|
2
|
-
// ua.mjs and publisher.mjs read package.json for the version; everything else
|
|
3
|
-
// (FileStorage, home, certs) is never constructed in the browser.
|
|
4
|
-
const PKG = JSON.stringify({ version: '0.18.0', name: 'fedipod' });
|
|
5
|
-
export const readFileSync = (p) => {
|
|
6
|
-
if (String(p).endsWith('package.json')) return PKG;
|
|
7
|
-
throw new Error(`node:fs readFileSync(${p}) is not available in the browser agent`);
|
|
8
|
-
};
|
|
9
|
-
export const existsSync = () => false;
|
|
10
|
-
const nope = (name) => () => { throw new Error(`node:fs ${name} is not available in the browser agent`); };
|
|
11
|
-
export const writeFileSync = nope('writeFileSync');
|
|
12
|
-
export const mkdirSync = nope('mkdirSync');
|
|
13
|
-
export const readdirSync = () => [];
|
|
14
|
-
export const statSync = nope('statSync');
|
|
15
|
-
export const unlinkSync = nope('unlinkSync');
|
|
16
|
-
export const rmSync = nope('rmSync');
|
|
17
|
-
export default { readFileSync, existsSync, writeFileSync, mkdirSync, readdirSync, statSync, unlinkSync, rmSync };
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// node-path.mjs — the POSIX slice of node:path the agent uses in the browser.
|
|
2
|
-
const normalize = (p) => {
|
|
3
|
-
const up = p.startsWith('/');
|
|
4
|
-
const parts = [];
|
|
5
|
-
for (const seg of p.split('/')) {
|
|
6
|
-
if (!seg || seg === '.') continue;
|
|
7
|
-
if (seg === '..') { if (parts.length && parts[parts.length - 1] !== '..') parts.pop(); else if (!up) parts.push('..'); }
|
|
8
|
-
else parts.push(seg);
|
|
9
|
-
}
|
|
10
|
-
return (up ? '/' : '') + parts.join('/') || (up ? '/' : '.');
|
|
11
|
-
};
|
|
12
|
-
const join = (...segs) => normalize(segs.filter((s) => s != null && s !== '').join('/'));
|
|
13
|
-
const dirname = (p) => { const i = p.replace(/\/$/, '').lastIndexOf('/'); return i <= 0 ? (i === 0 ? '/' : '.') : p.slice(0, i); };
|
|
14
|
-
const basename = (p, ext) => { let b = p.slice(p.lastIndexOf('/') + 1); if (ext && b.endsWith(ext)) b = b.slice(0, -ext.length); return b; };
|
|
15
|
-
const extname = (p) => { const b = basename(p); const i = b.lastIndexOf('.'); return i > 0 ? b.slice(i) : ''; };
|
|
16
|
-
const resolve = (...segs) => join(...segs);
|
|
17
|
-
const posix = { join, dirname, basename, extname, resolve, normalize, sep: '/' };
|
|
18
|
-
export { join, dirname, basename, extname, resolve, normalize, posix };
|
|
19
|
-
export default { join, dirname, basename, extname, resolve, normalize, posix, sep: '/' };
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
// node-url.mjs — the node:url slice the agent uses. URL is native; the two
|
|
2
|
-
// file-path converters are only ever called with data: or http URLs in the
|
|
3
|
-
// browser build, so plain string surgery suffices.
|
|
4
|
-
export const fileURLToPath = (u) => { const s = typeof u === 'string' ? u : u.href; return s.replace(/^file:\/\//, ''); };
|
|
5
|
-
export const pathToFileURL = (p) => new URL('file://' + (p.startsWith('/') ? p : '/' + p));
|
|
6
|
-
export { URL, URLSearchParams } from './_globals.mjs';
|
|
7
|
-
export default { URL: globalThis.URL, URLSearchParams: globalThis.URLSearchParams, fileURLToPath, pathToFileURL };
|
package/web/app/shims/prelude.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
// prelude.js — globals the bundled Node code expects, set once before it runs.
|
|
2
|
-
// Injected as the bundle's banner. Kept minimal and local: Buffer is a subclass
|
|
3
|
-
// of Uint8Array (not a prototype patch), so nothing else in the page is touched.
|
|
4
|
-
globalThis.process ??= { env: {}, argv: [], platform: 'browser', cwd: () => '/', nextTick: (f, ...a) => queueMicrotask(() => f(...a)) };
|
|
5
|
-
globalThis.Buffer ??= (() => {
|
|
6
|
-
const b64 = (u) => btoa(String.fromCharCode(...u));
|
|
7
|
-
const hex = (u) => { let s = ''; for (const b of u) s += b.toString(16).padStart(2, '0'); return s; };
|
|
8
|
-
class Buf extends Uint8Array {
|
|
9
|
-
// Uint8Array.indexOf finds a single BYTE; Node's Buffer finds a whole
|
|
10
|
-
// subsequence, and lib/mastoapi.mjs's readMultipart walks a multipart body
|
|
11
|
-
// by searching for the boundary and for the CRLFCRLF that ends each part's
|
|
12
|
-
// headers. Without this every search returned -1, so an upload came back
|
|
13
|
-
// "file required" however good the bytes were.
|
|
14
|
-
indexOf(needle, from = 0) {
|
|
15
|
-
const pat = typeof needle === 'string' ? new TextEncoder().encode(needle)
|
|
16
|
-
: needle instanceof Uint8Array ? needle : null;
|
|
17
|
-
if (pat === null) return super.indexOf(needle, from); // a single byte
|
|
18
|
-
if (pat.length === 0) return Math.min(from, this.length);
|
|
19
|
-
const last = this.length - pat.length;
|
|
20
|
-
outer: for (let i = Math.max(0, from); i <= last; i++) {
|
|
21
|
-
for (let j = 0; j < pat.length; j++) if (this[i + j] !== pat[j]) continue outer;
|
|
22
|
-
return i;
|
|
23
|
-
}
|
|
24
|
-
return -1;
|
|
25
|
-
}
|
|
26
|
-
toString(enc) {
|
|
27
|
-
if (enc === 'hex') return hex(this);
|
|
28
|
-
if (enc === 'base64') return b64(this);
|
|
29
|
-
if (enc === 'base64url') return b64(this).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
30
|
-
return new TextDecoder().decode(this);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
const fromString = (s, enc) => {
|
|
34
|
-
if (enc === 'hex') return Buf.from(s.match(/.{1,2}/g) || [], (h) => parseInt(h, 16));
|
|
35
|
-
if (enc === 'base64' || enc === 'base64url') {
|
|
36
|
-
const n = s.replace(/-/g, '+').replace(/_/g, '/');
|
|
37
|
-
return Buf.from(atob(n + '==='.slice((n.length + 3) % 4)), (c) => c.charCodeAt(0));
|
|
38
|
-
}
|
|
39
|
-
return Buf.from(new TextEncoder().encode(s));
|
|
40
|
-
};
|
|
41
|
-
const from = (d, enc) => {
|
|
42
|
-
if (typeof d === 'string') return fromString(d, enc);
|
|
43
|
-
if (typeof d === 'function') { const args = arguments; return Uint8Array.from.call(Buf, args[0], args[1]); }
|
|
44
|
-
const b = new Buf(d.length); b.set(d); return b;
|
|
45
|
-
};
|
|
46
|
-
const fromAny = (d, e) => {
|
|
47
|
-
if (typeof d === 'string') return fromString(d, e);
|
|
48
|
-
if (typeof e === 'function') { const arr = Array.from(d, e); const b = new Buf(arr.length); b.set(arr); return b; }
|
|
49
|
-
if (d instanceof Uint8Array || Array.isArray(d)) { const b = new Buf(d.length); b.set(d); return b; }
|
|
50
|
-
return new Buf(d);
|
|
51
|
-
};
|
|
52
|
-
return {
|
|
53
|
-
from: fromAny,
|
|
54
|
-
concat: (list) => { const n = list.reduce((s, x) => s + x.length, 0); const o = new Buf(n); let i = 0; for (const x of list) { o.set(x, i); i += x.length; } return o; },
|
|
55
|
-
alloc: (n) => new Buf(n),
|
|
56
|
-
isBuffer: (x) => x instanceof Uint8Array,
|
|
57
|
-
};
|
|
58
|
-
})();
|