mirror of
https://github.com/HeyPuter/puter.git
synced 2026-11-07 22:44:06 +00:00
54bfc0ffe50a46ef41f7307c15ef4d411cfec6cc
7367
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
54bfc0ffe5 |
test(puter-js): prove microphone chunks hit the closing-socket gap
The browser test now counts the chunks the transport refused while the socket read as closing, and requires at least one, so it can't pass without exercising the gap. The socket stays closing until the connection drops instead of going back to open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
c27883134b |
fix(puter-js): keep microphone capture when the socket closes first
A conversation still reads as connected for a moment after its socket starts closing. A microphone chunk sent in that moment got `session_closed`, and the capture handler treated that like a real media failure: it stopped the tracks and the AudioContext, so the replacement session after the reconnect had no microphone. The built-in microphone now drops a chunk the transport refuses with `session_closed` or `session_not_ready`, as it already did for `queue_full`, and keeps capturing. A reconnect resumes sending; a final close still stops capture through the conversation, and processor or other errors still release it. Dropped chunks aren't replayed, and public sends keep reporting the transport error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
cf4c1f6b64 |
fix(puter-js): end the conversation on an oversized event mid-reconnect
A server event over the 4 MiB limit closes the socket with 4009, which ends a connected conversation. During a reconnect attempt the same close was reported as `session_closed`, which is retried, so the conversation tried ten more times and finally closed with the original drop code. A replacement session opened after a restart notice was reopened again and again for the same reason. A 4009 close now ends the conversation during a reconnect too, reporting 4009, and a replacement session that hits it isn't reopened. Tests cover both, a multibyte event of exactly 4 MiB, and a whole retrieved item through the relay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
305d20ab1c |
fix(puter-js): accept provider events up to the relay's 4 MiB cap
The conversation transport refused any server event over 64 KiB with a terminal 4009 close, the limit meant for what the SDK sends. The relay forwards provider events up to 4 MiB, and a session.updated echo or a whole audio item easily passes 64 KiB, so valid conversations ended. Incoming events now have their own 4 MiB limit, matching the relay, counted in UTF-8 bytes (a string short enough that it can't exceed the limit isn't encoded to check). An event over it still closes with 4009 and doesn't reconnect. Uploads keep their 64 KiB cap, and malformed events are still ignored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0ce6b0646a |
perf(metering): stop releases from churning the reservation version
Every release bumped the balance version, so an account's own short-lived holds (each realtime session probes one and releases it at once) could keep its other reservations reading a stale version until they were refused. The usage a hold stands for already bumps the version when it's recorded, before the hold is released, and a release without usage leaves the balance as it was, so releases no longer bump it. The admission read also uses the cached add-ons record when it shows no purchased credit left, which can only understate the balance; it reads consistently only while credit remains. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
6b5c4f3d9e |
fix(metering): refuse a reservation whose balance never holds still
A reservation that found the balance changed three times in a row used to skip the version check on its fourth attempt, which brought back the race the check closes; an account could provoke that itself. It now waits a short randomized time between attempts and refuses after five. A missing version key now starts from a random value, so a version read before the key expired can't match its successor. Credit grants, allowance changes, manual usage corrections and a usage write that fails partway also bump the version, and the store refuses an amount it can't hold instead of storing a negative hold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
d6fdc5f268 |
fix(metering): reserve AI credit only when it fits, in one step
`reserveAiCredits` checked the balance and then took an unconditional hold, so concurrent requests could all pass the check before any hold was visible; two 16.2M µ¢ holds were admitted against a 20M µ¢ balance. The hold store now takes a hold only if it fits beside the live holds, in one Lua script on the actor's hold key. A per-user version, bumped when usage is recorded and when a hold is released, lets the script refuse a balance read that a settlement has since overtaken; the service reads the balance again and retries, and skips the version check on its last attempt rather than refuse an account whose balance keeps moving. Both keys share the actor's hash tag, so the script runs on Redis Cluster. The balance read for this check reads purchased credit consistently. `reserveCreditsIfAffordable` carries this through the service, and the AI facade calls it once with the cost factor applied. `reserveCredits` keeps its unconditional semantics. When the cache is unreachable, the balance alone decides and nothing is held, as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
4f3f946941 |
fix(realtime): judge a refusal by the plan it was decided on
A plan or funds refusal was classified by a plan lookup made after the decision. A fallback plan is cached for only a few seconds, so when it ran out in between, the refusal was judged against the recovered plan and still ended the session with a terminal 1008. The plan is now also checked just before each decision, and a refusal is retryable if either check found a fallback. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
4e36672b6b |
test(puter-js): fail credential lookups until the relay hits one
The reconnect test made the next credential lookup fail once, but the SDK's own socket.io connection could take that failure before the relay's reconnect attempt did, so the relay never saw a failed lookup and the test failed in CI. Lookups now fail until the relay has refused an attempt on one, then recover. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
6b0b530ab1 |
fix(realtime): retry instead of refusing when the plan lookup fails
When the billing lookup failed, metering answered with a provisional fallback plan, so the relay refused the session with `subscription_required` or, against the fallback's allowance, `insufficient_funds`, both terminal 1008 closes, and the SDK showed the upgrade prompt to a paying account. A plan or funds refusal decided on a provisional plan now sends `internal_error` and closes with 1011, at connection and mid-session, so the client retries once the lookup recovers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
68b8f341e9 |
test(realtime): cover a revoked session credential
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
c3756d3ac3 |
fix(realtime): tell transient server failures apart from bad credentials
A credential lookup that threw (a database or cache outage) was treated as no actor, so the session got `invalid_api_key` and a 1008 close. That is terminal for the SDK, which then ended an established conversation that only needed to reconnect. A failed lookup now gets `internal_error` and closes with 1011, which the SDK retries; missing, invalid and revoked credentials keep `invalid_api_key` and 1008. Nothing is cached for the failed lookup, and the cause is logged server-side only. A session whose usage records pile up behind metering now closes with 1013 instead of 1008, still with `rate_limit_exceeded`. Queued usage is still recorded and its holds released. An unexpected setup failure now sends `internal_error` before its 1011 close. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0c927ac5a4 |
fix(realtime): listen for client socket errors before refusing a session
A session refused while the server drains returned before its client error and close listeners were registered, so a frame that errored the socket during the close handshake (an oversized one, for example) emitted an unhandled `error` and ended the process. The listeners now go on as soon as the session exists, ahead of every refusal. The 404 answer to an upgrade for another host also ends a raw socket that has no error listener once Node hands it over; it now has one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
64c3b3c0ce | subdomain and worker recipes complex, writing full stack applications with both (#4206) | ||
|
|
e9a4c7a461 |
ai docs cleanup (#4504)
release-please / release-please (push) Canceled after 0s
Backend Tests / changes (push) Canceled after 0s
Backend Tests / typecheck (push) Canceled after 0s
Backend Tests / test shard 1/4 (push) Canceled after 0s
Backend Tests / test shard 2/4 (push) Canceled after 0s
Backend Tests / test shard 3/4 (push) Canceled after 0s
Backend Tests / test shard 4/4 (push) Canceled after 0s
Backend Tests / test (main) (push) Canceled after 0s
Backend Tests / coverage (push) Canceled after 0s
Maintain Release Merge PR / update-release-pr (push) Canceled after 0s
Notify HeyPuter / notify (push) Canceled after 0s
* ai docs cleanup * fixes |
||
|
|
8b64cc5076 |
docs(notifications): recipes and format pass for dismissal, priority, buttons and schedule updates (#4502)
- dismiss.md is the one place that explains closing a notification on other devices; on/onNotification/push link to it instead of repeating it. - Pages follow the house format: section order, error tables, playground examples for dismiss() and markShown(), sidebar and index listings. - updateSchedule.md describes what a developer sees instead of internals. - New recipes: mark notifications as read, incoming calls (high priority, short ttl, buttons), unread counts on the app icon. - Existing recipes move reminders, snoozes and recurring summaries with updateSchedule() instead of cancel-and-recreate; the buttons recipe follows the recipe format. |
||
|
|
d3869b7846 |
feat(notifications): dismissal across devices, high priority, buttons, schedule updates, docs fixes (#4501)
- Docs: status(), browser limits, events worker fetch/ack, update() across
pages, stored-notification retries, device removal, push delivery, queue
coalescing, existing service workers and missing error codes match the code.
- puter.notifications.markShown(uid) / dismiss(uid).
- Dismissals reach the app's live feed (on('dismiss')); pages close the
notification on screen, and a later page load closes ones already dismissed.
The desktop closes its own copies on notif.ack.
- Live notification postings are forwarded to sessions in other regions.
- notify({ priority: 'high' }).
- onPush() results accept requireInteraction and actions; clicks carry action.
- replaced reaches service-worker onPush() handlers.
- puter.events.updateSchedule(scheduleId, changes), with the invoker
re-checking a schedule run's guard right before it is sent.
|
||
|
|
91230697cd |
fix(backend): shutdown hooks don't skip each other, and final flushes wait for in-flight work (#4499) (#4500)
Backend Tests / changes (push) Canceled after 0s
Backend Tests / typecheck (push) Canceled after 0s
Backend Tests / test shard 1/4 (push) Canceled after 0s
Backend Tests / test shard 2/4 (push) Canceled after 0s
Backend Tests / test shard 3/4 (push) Canceled after 0s
Backend Tests / test shard 4/4 (push) Canceled after 0s
Backend Tests / test (main) (push) Canceled after 0s
Backend Tests / coverage (push) Canceled after 0s
Maintain Release Merge PR / update-release-pr (push) Canceled after 0s
Notify HeyPuter / notify (push) Canceled after 0s
release-please / release-please (push) Canceled after 0s
A throwing prepare/shutdown hook stopped the loop, so shutdown() skipped closeAllConnections and every later hook, including the final metering flush; each hook now runs and failures are logged. Broadcast, events forwarding and share digests joined nothing already in flight and could drop what was queued behind it; their final flushes now wait for it, ship the tail, and drain once more at shutdown. Local worker disposal and the OpenAI image metering call are awaited. settleWithin and FINAL_FLUSH_TIMEOUT_MS bound each final flush. |
||
|
|
56e4856518 |
feat(backend): drain-start hooks run concurrently and can hold the listener open (#4498)
The listener closes once every onServerDrainStart hook settles, and the drain window now starts at the signal, so time a hook spends holding the listener open counts against drainMs instead of extending shutdown. A failing hook no longer skips the others. |
||
|
|
8acd73675c | use gpt 6 luna as example in docs (#4497) | ||
|
|
fc4af97885 |
fix(gui): the custom domain empty state holds its add button
With no domains, the only way on was the add button in the footer, which the full-page popup pins to the bottom edge, far from the empty state the eye lands on. The button now sits in the empty state under its hint, and the footer keeps only Cancel until there's a domain to list. |
||
|
|
7cec710014 |
feat(gui): redesign the custom domain dialog, full page in its popup (#4496)
* feat(gui): redesign the custom domain dialog, full page in its popup
- Domains list as cards with a status badge, trash icon, and inline
Cancel/Confirm for remove and move
- DNS records as cards with copy buttons that confirm with a check
- Status card on the records step; a green check once the domain serves
the site, after which the dialog closes
- Spinners on the button that's working, skeleton while loading, empty
and unavailable states
- Pasted URLs are trimmed to their hostname before adding
- In the attach-custom-domain popup the dialog fills the page, with the
actions pinned to the bottom
* fix(gui): Escape backs out of removing the chosen custom domain
Escape already put back a list row's pending confirmation. On the records
step, it closed the dialog while Remove was asking to confirm; it now puts
the button back first, like the rows.
* fix(gui): each custom domain step opens scrolled to the top
Switching steps reset the scroll of the dialog's body, which only scrolls
when the dialog fills a popup. The floating modal scrolls itself, so a
step opened from far down the list started scrolled past its status card.
* fix(gui): keyboard focus stays in the custom domain dialog
Focus on a control that got hidden or disabled fell to the page behind the
dialog, so Tab started over from there. Switching steps now focuses the
dialog, backing out of a row's confirmation returns to the button that
asked, a request gives focus back to the button that started it, and
removing a row moves focus to the next one.
* fix(gui): a removed custom domain row takes no input as it fades
Its buttons came back on once the delete finished, so a second click in
the fade sent the delete again, and Cancel brought back Use for a domain
that no longer exists. The row is inert until it's gone.
* fix(gui): DNS record copy buttons work without the clipboard API
On an origin with no navigator.clipboard (a Puter served over plain http)
or when the browser refuses the write, the copy buttons did nothing. They
now select the value and try the legacy copy, like the dashboard's form
dialog, and leave it selected to copy by hand if that fails too.
* fix(gui): custom domain rows keep the icon beside the name on phones
The name was sized to the row less 46px for the icon and gap, but the icon
is 36px wide with its border, so the row overflowed by 2px and every name
dropped below its icon. Only the actions wrap now; the name takes the rest
of the first line.
* fix(gui): hostnames in the custom domain dialog wrap whole
Every bold hostname broke at any letter, so one that would fit on the
next line was split mid-word at the end of this one ("puter.lo / calhost").
It now moves down whole and breaks only when it's wider than the line.
* fix(gui): readable Remove and Confirm buttons in dark mode
The red fill under their white labels used the danger text color, which
dark mode lightens to #f87171: 2.8:1 against white. Fills now use their
own token that stays #dc2626 (4.8:1); light mode is unchanged.
* fix(gui): the switch-to-www link shows when it's working
The link that switches a root domain to www had no disabled or loading
style, so while its request ran it still looked clickable and showed
nothing, while every other control dimmed. It now dims like them when
waiting and shows a spinner when it's the one working.
|
||
|
|
48cd4ea355 |
fix: oidc toggle (#4494)
Backend Tests / changes (push) Canceled after 0s
Backend Tests / typecheck (push) Canceled after 0s
Backend Tests / test shard 1/4 (push) Canceled after 0s
Backend Tests / test shard 2/4 (push) Canceled after 0s
Backend Tests / test shard 3/4 (push) Canceled after 0s
Backend Tests / test shard 4/4 (push) Canceled after 0s
Backend Tests / test (main) (push) Canceled after 0s
Backend Tests / coverage (push) Canceled after 0s
Maintain Release Merge PR / update-release-pr (push) Canceled after 0s
Notify HeyPuter / notify (push) Canceled after 0s
release-please / release-please (push) Canceled after 0s
|
||
|
|
a110be0340 |
feat(gui): remove a custom domain from the attach dialog (#4493)
Each domain in the list gets a Remove button, confirmed in place like Move, and the records pane can remove the domain just added. A removed domain is never handed back as the dialog's answer. Also fixes two strings that went stale: the claim prompt (an active domain can be claimed too now) and the redirect note (root domains get two A records). PUT-2338 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
93798cba93 |
fix(puter-js): saved push handlers stay while any tab uses them, and the worker can skip them (#4481)
* fix(puter-js): background push syncs never prompt for sign-in The load-time sync and the resync after the browser replaces a subscription ask for the key and announce the subscription without opening Puter's sign-in; only enable() may. The confirmation and the removal never open it, and looking up the user for the opt-in uses a non-interactive /whoami. A rejected token during a background sync is not reported as a sync failure. * fix(puter-js): turning push off in one tab stops other tabs' syncs Tabs of a site take a shared Web Lock to subscribe and announce, and again to confirm, without holding it while the confirmation push is on its way. A load sync re-reads the opt-in inside the lock and stops when it is gone. disable() tells the other tabs first, so their syncs and any enable() in flight stop, then forgets the opt-ins and removes the subscription inside the same lock. Without Web Locks the re-reads and the message still apply. * fix(puter-js): wait for a push confirmation only once the lock is held A sync queued behind another tab's lock started its 30 second confirmation timer and listener before it got the lock, so a long wait failed the sync or left the timeout unhandled. The listener now starts inside the locked step, right before the subscription is announced. * test(puter-js): reach the push sync in the boot sign-in e2e The push page now has the user's opt-in and cached user from an earlier load, starts its sync before boot's own requests come back, and gets a rejected token from the push routes, so the spec covers the key request that must not open a sign-in. * fix(puter-js): sign-out and disable() remove only Puter's push subscription Removal now looks at the subscription's application server key and ends it only when it is Puter's: the key stored at the last subscribe, or the server's key when none is stored. Any other subscription on the site is left as it is and the server is not told about it. Without a named worker, as at sign-out or disable() without serviceWorker, every registration on the origin is checked, so a subscription under a folder worker is the one removed. * fix(puter-js): sign-out and a user change end this browser's pushes Sign-out stops this tab's syncs, tells the site's other tabs, and inside the push lock forgets every opt-in and tap, ends Puter's subscription in the browser, then sends the removal with the leaving user's token as a keepalive request so it is still sent if the page reloads. puter.auth.signOut() returns a promise that resolves once that is done; the token is still discarded at once. A different user signing in over a session, or after the token was dropped, gets the same cleanup with the token of the user signed in before. A new token for the same user, or a dropped token, keeps everything. A page load finding a Puter subscription with no opt-in for the signed-in user, or with nobody signed in, ends it in the browser. Opt-in records that can't be read leave it as it is. * fix(puter-js): record a push opt-in before other tabs can check it enable() now writes the user's opt-in inside the push lock, right after the server has the subscription and before the lock is released. A tab loading at the same time waits for the lock, reads the opt-in, and keeps the new subscription. A failed announce, or push turned off while it ran, records nothing. * fix(puter-js): push cleanup runs without the lock where Web Locks refuse In a frame where navigator.locks.request rejects, such as a sandboxed frame, sign-out, disable(), the load-time check and syncs now run their step without the lock instead of rejecting, so signOut() still resolves and the subscription is still ended. * fix(puter-js): send the sign-out removal again when keepalive is refused A keepalive request that needs a CORS preflight is refused by some browsers. The removal at sign-out is then sent once more as a normal request with the same token and without a sign-in prompt, after the browser has already ended the subscription. * test(puter-js): run the push boot test on full Chromium and skip it without push support The headless shell keeps Notification.permission at 'denied' after the permission is granted, so onPush() never synced there and the test saw no key request. The test now launches full Chromium itself, since a channel can't be set per test, and is skipped on WebKit and wherever the page has no PushManager or notification permission. * fix(puter-js): a user change finishes its push cleanup before enable() enable() waits for the cleanup a user change started. When the token was dropped and a different user signs in from enable(), the pushes of the user signed in before end first, and the new user keeps their new opt-in and subscription. A token that sign-out already cleaned up is not cleaned up again when the next user signs in. * fix(puter-js): a tap without a URL opens the site the push came from A page that calls onPush() or enable() saves where taps resolve for its worker: the worker's folder when the page is inside it, else the site's root. push-sw.js and the notifications a page shows itself read relative URLs from it, and fall back to the worker's scope when nothing is saved. * fix(puter-js): push taps go to top-level windows only push-sw.js routes taps, and holds back a page's notification for a focused window, among top-level windows only, as the GUI's worker does. Challenges and subscription changes still reach every window of the origin. * fix(puter-js): deliver a push tap to the page once push-sw.js keeps a tap before posting it to an open page. The page runs its click handler only after taking that copy out of IndexedDB in one readwrite transaction, and acknowledges only then. When no acknowledgement comes in time, the worker takes the copy itself and opens the tap's URL only if the page hadn't. The wait starts at the post and ends at most 800 ms after the click, so openWindow still runs in time. Pages and workers without this keep today's behaviour. * fix(puter-js): clear stored push taps that nobody reads push-sw.js removes taps kept over five minutes on every push and tap, and a page sweeps them once when onPush() syncs. A page opened for a tap ignores a kept tap older than that, and removes it when no click handler is added within 10 seconds of loading. * fix(puter-js): a late page already at the tap's URL still gets the tap When no acknowledgement comes in time and the window is already at the tap's URL, push-sw.js leaves the copy it kept for that page instead of taking it back, so the page's click handler still runs when it answers late. A copy nobody takes is removed with the other old taps. * fix(puter-js): a push tap runs at most once when IndexedDB fails A page that can't take a kept tap out of IndexedDB leaves it to the worker instead of running it, and a worker whose claim fails leaves it to the page instead of opening the tap's URL. * fix(puter-js): take a tap back and keep it for the next page in one step When no acknowledgement comes in time, push-sw.js takes its copy back and keeps it, without the id, for the page it opens in one readwrite transaction, leaving more of Firefox's window-opening allowance for openWindow. * test(puter-js): shorten the fake IndexedDB comment * test(puter-js): keep the push boot test comment to one line * fix(puter-js): a user signing back in with the same token gets the user-change cleanup again The token that sign-out already cleaned up is skipped only for the next user change. Once the same token is set again, a later sign-in by a different user ends its pushes as usual. * fix(static): push-sw.js refreshes at the CDN within five minutes * feat(puter-js): the push worker can skip saved handlers * fix(puter-js): off() keeps a saved push handler another tab still uses * docs(notifications): self-hosting the push worker * docs(notifications): name the pinned push worker version as a placeholder * fix(puter-js): pages with push handlers can enter the back/forward cache * test(puter-js): a restored page saves its push handler again * docs(notifications): pin the push worker only with a pinned puter.js |
||
|
|
848e336455 |
fix(puter-js): page loads check push status instead of registering again (#4480)
* fix(puter-js): background push syncs never prompt for sign-in The load-time sync and the resync after the browser replaces a subscription ask for the key and announce the subscription without opening Puter's sign-in; only enable() may. The confirmation and the removal never open it, and looking up the user for the opt-in uses a non-interactive /whoami. A rejected token during a background sync is not reported as a sync failure. * fix(puter-js): turning push off in one tab stops other tabs' syncs Tabs of a site take a shared Web Lock to subscribe and announce, and again to confirm, without holding it while the confirmation push is on its way. A load sync re-reads the opt-in inside the lock and stops when it is gone. disable() tells the other tabs first, so their syncs and any enable() in flight stop, then forgets the opt-ins and removes the subscription inside the same lock. Without Web Locks the re-reads and the message still apply. * fix(puter-js): wait for a push confirmation only once the lock is held A sync queued behind another tab's lock started its 30 second confirmation timer and listener before it got the lock, so a long wait failed the sync or left the timeout unhandled. The listener now starts inside the locked step, right before the subscription is announced. * test(puter-js): reach the push sync in the boot sign-in e2e The push page now has the user's opt-in and cached user from an earlier load, starts its sync before boot's own requests come back, and gets a rejected token from the push routes, so the spec covers the key request that must not open a sign-in. * fix(puter-js): sign-out and disable() remove only Puter's push subscription Removal now looks at the subscription's application server key and ends it only when it is Puter's: the key stored at the last subscribe, or the server's key when none is stored. Any other subscription on the site is left as it is and the server is not told about it. Without a named worker, as at sign-out or disable() without serviceWorker, every registration on the origin is checked, so a subscription under a folder worker is the one removed. * fix(puter-js): sign-out and a user change end this browser's pushes Sign-out stops this tab's syncs, tells the site's other tabs, and inside the push lock forgets every opt-in and tap, ends Puter's subscription in the browser, then sends the removal with the leaving user's token as a keepalive request so it is still sent if the page reloads. puter.auth.signOut() returns a promise that resolves once that is done; the token is still discarded at once. A different user signing in over a session, or after the token was dropped, gets the same cleanup with the token of the user signed in before. A new token for the same user, or a dropped token, keeps everything. A page load finding a Puter subscription with no opt-in for the signed-in user, or with nobody signed in, ends it in the browser. Opt-in records that can't be read leave it as it is. * fix(puter-js): record a push opt-in before other tabs can check it enable() now writes the user's opt-in inside the push lock, right after the server has the subscription and before the lock is released. A tab loading at the same time waits for the lock, reads the opt-in, and keeps the new subscription. A failed announce, or push turned off while it ran, records nothing. * fix(puter-js): push cleanup runs without the lock where Web Locks refuse In a frame where navigator.locks.request rejects, such as a sandboxed frame, sign-out, disable(), the load-time check and syncs now run their step without the lock instead of rejecting, so signOut() still resolves and the subscription is still ended. * fix(puter-js): send the sign-out removal again when keepalive is refused A keepalive request that needs a CORS preflight is refused by some browsers. The removal at sign-out is then sent once more as a normal request with the same token and without a sign-in prompt, after the browser has already ended the subscription. * test(puter-js): run the push boot test on full Chromium and skip it without push support The headless shell keeps Notification.permission at 'denied' after the permission is granted, so onPush() never synced there and the test saw no key request. The test now launches full Chromium itself, since a channel can't be set per test, and is skipped on WebKit and wherever the page has no PushManager or notification permission. * fix(puter-js): a user change finishes its push cleanup before enable() enable() waits for the cleanup a user change started. When the token was dropped and a different user signs in from enable(), the pushes of the user signed in before end first, and the new user keeps their new opt-in and subscription. A token that sign-out already cleaned up is not cleaned up again when the next user signs in. * fix(puter-js): a tap without a URL opens the site the push came from A page that calls onPush() or enable() saves where taps resolve for its worker: the worker's folder when the page is inside it, else the site's root. push-sw.js and the notifications a page shows itself read relative URLs from it, and fall back to the worker's scope when nothing is saved. * fix(puter-js): push taps go to top-level windows only push-sw.js routes taps, and holds back a page's notification for a focused window, among top-level windows only, as the GUI's worker does. Challenges and subscription changes still reach every window of the origin. * fix(puter-js): deliver a push tap to the page once push-sw.js keeps a tap before posting it to an open page. The page runs its click handler only after taking that copy out of IndexedDB in one readwrite transaction, and acknowledges only then. When no acknowledgement comes in time, the worker takes the copy itself and opens the tap's URL only if the page hadn't. The wait starts at the post and ends at most 800 ms after the click, so openWindow still runs in time. Pages and workers without this keep today's behaviour. * fix(puter-js): clear stored push taps that nobody reads push-sw.js removes taps kept over five minutes on every push and tap, and a page sweeps them once when onPush() syncs. A page opened for a tap ignores a kept tap older than that, and removes it when no click handler is added within 10 seconds of loading. * fix(puter-js): a late page already at the tap's URL still gets the tap When no acknowledgement comes in time and the window is already at the tap's URL, push-sw.js leaves the copy it kept for that page instead of taking it back, so the page's click handler still runs when it answers late. A copy nobody takes is removed with the other old taps. * fix(puter-js): a push tap runs at most once when IndexedDB fails A page that can't take a kept tap out of IndexedDB leaves it to the worker instead of running it, and a worker whose claim fails leaves it to the page instead of opening the tap's URL. * fix(puter-js): take a tap back and keep it for the next page in one step When no acknowledgement comes in time, push-sw.js takes its copy back and keeps it, without the id, for the page it opens in one readwrite transaction, leaving more of Firefox's window-opening allowance for openWindow. * test(puter-js): shorten the fake IndexedDB comment * test(puter-js): keep the push boot test comment to one line * fix(puter-js): a user signing back in with the same token gets the user-change cleanup again The token that sign-out already cleaned up is skipped only for the next user change. Once the same token is set again, a later sign-in by a different user ends its pushes as usual. * fix(puter-js): a page load checks push status instead of registering again * fix(puter-js): subscriptions made with an earlier Puter key are replaced * fix(puter-js): a replaced browser subscription is picked up on the next visit * fix(puter-js): a page load that only checks push status still looks for a newer worker * fix(puter-js): the worker subscribes again only where a Puter page used it * docs(puter-js): soften the note on subscriptions Puter made earlier * fix(puter-js): a status answer on load leaves a subscription another tab just turned on |
||
|
|
a9a8df6409 |
feat(events): fetch can start at the newest notification, and the feed uses it (#4479)
* feat(events): fetch can start from the newest event GET /events/fetch takes from=end: with no cursor it answers an empty page whose cursor sits at the newest row of the slice, so later reads return only rows created or replaced after it. puter.events.fetch() passes `from` through. Adds a (user_id, audience, app_uid, id) index on notification so the newest row of a slice is one seek. * fix(puter-js): onPush starts its feed from the newest notification A notifications listener started without catch-up asks for from: 'end' and keeps the cursor it gets back instead of reading every stored notification to mark them as seen. A server that ignores `from` answers with the oldest page, and the listener reads through it as before. * fix(events): read the newest notification id from the primary * fix(events): mark the end cursor at the slice's latest replacement The end cursor's replacement mark is the newest (replaced_at, id) in the slice rather than the serving node's clock, since replaced_at is stamped by whichever node wrote it. * docs(events): describe the end cursor's replacement mark |
||
|
|
0a8b37ae56 |
fix(notifications): website apps are named by their hostname (#4468)
An app created for an origin keeps its uid as name and title, so its notifications showed that uid as the sender. appMessageSender now names such an app by the hostname of its index_url, and keeps the stored name when index_url doesn't parse. The check AppDriver used for these rows moves to util/appView so both share it. The Settings app list shows a generic label for an app whose title is still its uid. |
||
|
|
23782dec12 |
feat(events): inline handlers in the user's own scope for onSchedule and onPersistent (#4487)
release-please / release-please (push) Canceled after 0s
Backend Tests / changes (push) Canceled after 0s
Backend Tests / typecheck (push) Canceled after 0s
Backend Tests / test shard 1/4 (push) Canceled after 0s
Backend Tests / test shard 2/4 (push) Canceled after 0s
Backend Tests / test shard 3/4 (push) Canceled after 0s
Backend Tests / test shard 4/4 (push) Canceled after 0s
Backend Tests / test (main) (push) Canceled after 0s
Backend Tests / coverage (push) Canceled after 0s
Maintain Release Merge PR / update-release-pr (push) Canceled after 0s
Notify HeyPuter / notify (push) Canceled after 0s
* feat(events): schema for user-scope handlers Add event_user_handlers and event_subscriptions.handler_scope in all three dialects. event_handlers is unchanged. * feat(events): scope the handler and durable subscription stores Handler store methods take a scope (app, or a user's scope of an app) and read the matching table. App-scope behavior, SQL and cache keys are unchanged; user scopes get their own cache keys, a 20-handler cap, and the inline @<sha256> name. Adds user-scope listing, counting, deletion and unbound-handler reads. Durable subscriptions store and return handlerScope, and listByHandler filters by scope and holder. * fix(events): hold the user-scope handler cap under concurrency and drop all scope caches on app delete A create that lands past the cap removes itself and is refused. deleteForApp drops each holder's cached set in batches, here and on peers. Batch scans over user scopes and unbound handlers take limit and cursor. * feat(events): set a CPU limit on events worker deploys * test(events): cover redeploy and resend of an evicted events script * test(events): name the deploy-on-miss test for what it checks * fix(events): keep a deploy-on-miss call inside the delivery lease * feat(workers): cap rented units per plan and refuse only new ones * feat(events): publish handlers into a user scope, with inline create * test(events): cover user-scope publish, inline create and listing * test(workers): name the credentials gate without a vendor * feat(events): run user-scope handlers as the holder A user scope deploys one script per handler under the holder's own identity, throttled per scope, with an eager deploy after publish that skips an upload another holder already made. Delivery and schedule runs address the holder's handler, carry the holder on the invoke headers, batch calls, the miss handler and the rehydrate route, and stay owed while the flag is off. Suspensions of a user scope notify the holder, teardown (app delete, consent revoke, account delete) drops the scope, and a job deletes unbound handlers after a day. * test(events): cover user-scope deploy, address, notices, teardown and the sweep Runs an inline user-scope handler through the local worker runtime as its holder, and covers the holder on the wire, the flag-off path, revoke and account-delete teardown, and the unbound-handler sweep. * test(events): a lagging replica read does not suspend a user-scope schedule run * feat(puter-js): inline user-scope handlers and scope option for handlers and workers * docs(events): document inline and user-scope handlers, with playground examples Cover inline handlers on onSchedule and onPersistent, handlerScope, the scope option on handlers and workers, the user-scope limits and rented units, and the new error codes. Add six playground examples and pin the new limit constants in limits.test.ts. * feat(schedules): carry the handler scope on the schedule view * docs(events): describe user-handler cleanup, schedule handlerScope and suspension notices * fix(events): gate the rented-unit cap on user handlers and let an account session destroy its own user scope * fix(events): keep user-scope rows off app handlers across mixed versions and bound their deploys and reads Cached durable rows carry an explicit scope marker; an unmarked entry with a handler is resolved from the table and rewritten. User-scope rows are parked before credit checks, billing, client delivery and suspension while the flag is off. Delivery-time handler checks and addressing share the replica cache entry. Concurrent eager deploys of one user-scope script upload once. * fix(events): keep a user scope's age across handler churn for rent event_user_handlers.scope_since records when the scope first held a handler. A new row takes the scope's existing minimum, so replacing code does not make the scope look newer to the monthly sweep. The count uses it instead of the oldest remaining handler's created_at. * fix(events): drop cached entries no row backs; document account-session scope destroy * test(jobs): list the user-handler sweep among the system jobs * fix(events): let the user-handlers migration apply on its own * fix(events): fail a cached-row read whose scope lookup fails instead of returning app-scope rows * fix(events): touch a reused user-scope handler before binding so the sweep cannot take it * fix(events): bound the deploy-on-miss wait by the call's deadline |
||
|
|
6a6cf78c03 |
fix(drivers): stream results with pipe so responses stay under the close-listener limit (#4485)
stream.pipeline adds seven close listeners to the response; with the ones metering, compression and tracing add, every streamed /drivers/call passed Node's ten-listener limit and logged MaxListenersExceededWarning. pipe plus explicit error and close handling adds two, and keeps the same behavior: a source failing mid-body aborts the response and logs once, and a client hanging up stops the source. |
||
|
|
8264837d9f |
feat(gui): type a path into the dashboard Files path bar (#4486)
Maintain Release Merge PR / update-release-pr (push) Canceled after 0s
release-please / release-please (push) Canceled after 0s
Notify HeyPuter / notify (push) Canceled after 0s
Backend Tests / changes (push) Canceled after 0s
Backend Tests / typecheck (push) Canceled after 0s
Backend Tests / test shard 1/4 (push) Canceled after 0s
Backend Tests / test shard 2/4 (push) Canceled after 0s
Backend Tests / test shard 3/4 (push) Canceled after 0s
Backend Tests / test shard 4/4 (push) Canceled after 0s
Backend Tests / test (main) (push) Canceled after 0s
Backend Tests / coverage (push) Canceled after 0s
* feat(gui): type a path into the dashboard Files path bar Clicking the path bar anywhere but on a crumb, or typing /, swaps the breadcrumbs for a text field. Enter opens the folder typed (or a file's folder with the file selected); ~ is home and relative paths start from the folder on screen. Escape or clicking away puts the crumbs back, and a path to nothing shows an alert and keeps the field open. * fix(gui): closing the path alert with Escape gives the path field back UIAlert only settles when a button is clicked, so an alert closed with Escape left the path field waiting forever: it no longer closed on blur and never got the focus back. Wait for the alert window to close instead. * fix(gui): look a typed path up once, however often Enter is pressed A second Enter (or a held key) during the lookup started another one, and for a path to nothing stacked a second alert on the first. * fix(gui): leave Enter and Escape to an IME composing in the path field Picking a candidate with Enter while typing a name through an input method went to the typed path, and Escape to cancel the composition closed the field. * fix(gui): arrow keys go on from rows the Files tab selects for you Selecting a typed file, or the items just uploaded or pasted, left the keyboard's starting row on whatever was selected before, so Down jumped next to that row instead of the new selection. * fix(gui): opening the path field no longer moves a narrow Files header On phones and in narrow lists the field was shorter than the crumb row it replaced and dropped its divider, so the buttons and the listing jumped up as it opened. It now takes a crumb's height and keeps the divider. * fix(gui): space the path field from the Files toolbar buttons Its border ran up against the sort button, a few pixels from the icon, where the toolbar's other items keep 10px apart. * fix(gui): phone keyboards leave a typed path as typed iOS autocorrect rewrote folder names in the path field as they were typed. Turn it off, as the GUI's other name fields do, and label the keyboard's Enter key Go. * fix(gui): a typed / or .. reaches the root folder The root can't be stat'ed, so the path field reported "/" as not found, even though Up goes there. Like Shared, it is now opened without a lookup. * fix(gui): Escape calls off a path lookup still running Escape closed the path field, but the lookup behind it carried on: the tab still went to the path once it answered, or alerted that it wasn't found, and a path typed into the reopened field was ignored until then. |
||
|
|
eb92421a59 |
fix: disallow puter emails from making new puter accounts (#4482)
release-please / release-please (push) Canceled after 0s
Backend Tests / changes (push) Canceled after 0s
Backend Tests / typecheck (push) Canceled after 0s
Backend Tests / test shard 1/4 (push) Canceled after 0s
Backend Tests / test shard 2/4 (push) Canceled after 0s
Backend Tests / test shard 3/4 (push) Canceled after 0s
Backend Tests / test shard 4/4 (push) Canceled after 0s
Backend Tests / test (main) (push) Canceled after 0s
Backend Tests / coverage (push) Canceled after 0s
Maintain Release Merge PR / update-release-pr (push) Canceled after 0s
Notify HeyPuter / notify (push) Canceled after 0s
|
||
|
|
e0af3361ed |
ci: add manual dev notify workflow (#4477)
* ci: add manual dev notify workflow * ci: allow choosing puter ref * ci: restrict dev notify to maintainers, scope dispatch token to an environment |
||
|
|
9bd7ba7079 |
fix(puter-js): push taps open the right page, once (#4472)
* fix(puter-js): a tap without a URL opens the site the push came from A page that calls onPush() or enable() saves where taps resolve for its worker: the worker's folder when the page is inside it, else the site's root. push-sw.js and the notifications a page shows itself read relative URLs from it, and fall back to the worker's scope when nothing is saved. * fix(puter-js): push taps go to top-level windows only push-sw.js routes taps, and holds back a page's notification for a focused window, among top-level windows only, as the GUI's worker does. Challenges and subscription changes still reach every window of the origin. * fix(puter-js): deliver a push tap to the page once push-sw.js keeps a tap before posting it to an open page. The page runs its click handler only after taking that copy out of IndexedDB in one readwrite transaction, and acknowledges only then. When no acknowledgement comes in time, the worker takes the copy itself and opens the tap's URL only if the page hadn't. The wait starts at the post and ends at most 800 ms after the click, so openWindow still runs in time. Pages and workers without this keep today's behaviour. * fix(puter-js): clear stored push taps that nobody reads push-sw.js removes taps kept over five minutes on every push and tap, and a page sweeps them once when onPush() syncs. A page opened for a tap ignores a kept tap older than that, and removes it when no click handler is added within 10 seconds of loading. * fix(puter-js): a late page already at the tap's URL still gets the tap When no acknowledgement comes in time and the window is already at the tap's URL, push-sw.js leaves the copy it kept for that page instead of taking it back, so the page's click handler still runs when it answers late. A copy nobody takes is removed with the other old taps. * fix(puter-js): a push tap runs at most once when IndexedDB fails A page that can't take a kept tap out of IndexedDB leaves it to the worker instead of running it, and a worker whose claim fails leaves it to the page instead of opening the tap's URL. * fix(puter-js): take a tap back and keep it for the next page in one step When no acknowledgement comes in time, push-sw.js takes its copy back and keeps it, without the id, for the page it opens in one readwrite transaction, leaving more of Firefox's window-opening allowance for openWindow. * test(puter-js): shorten the fake IndexedDB comment |
||
|
|
8f3d4cc40f |
fix(puter-js): sign-out ends Puter's push subscription in this browser (#4471)
* fix(puter-js): sign-out and disable() remove only Puter's push subscription Removal now looks at the subscription's application server key and ends it only when it is Puter's: the key stored at the last subscribe, or the server's key when none is stored. Any other subscription on the site is left as it is and the server is not told about it. Without a named worker, as at sign-out or disable() without serviceWorker, every registration on the origin is checked, so a subscription under a folder worker is the one removed. * fix(puter-js): sign-out and a user change end this browser's pushes Sign-out stops this tab's syncs, tells the site's other tabs, and inside the push lock forgets every opt-in and tap, ends Puter's subscription in the browser, then sends the removal with the leaving user's token as a keepalive request so it is still sent if the page reloads. puter.auth.signOut() returns a promise that resolves once that is done; the token is still discarded at once. A different user signing in over a session, or after the token was dropped, gets the same cleanup with the token of the user signed in before. A new token for the same user, or a dropped token, keeps everything. A page load finding a Puter subscription with no opt-in for the signed-in user, or with nobody signed in, ends it in the browser. Opt-in records that can't be read leave it as it is. * fix(puter-js): record a push opt-in before other tabs can check it enable() now writes the user's opt-in inside the push lock, right after the server has the subscription and before the lock is released. A tab loading at the same time waits for the lock, reads the opt-in, and keeps the new subscription. A failed announce, or push turned off while it ran, records nothing. * fix(puter-js): push cleanup runs without the lock where Web Locks refuse In a frame where navigator.locks.request rejects, such as a sandboxed frame, sign-out, disable(), the load-time check and syncs now run their step without the lock instead of rejecting, so signOut() still resolves and the subscription is still ended. * fix(puter-js): send the sign-out removal again when keepalive is refused A keepalive request that needs a CORS preflight is refused by some browsers. The removal at sign-out is then sent once more as a normal request with the same token and without a sign-in prompt, after the browser has already ended the subscription. * fix(puter-js): a user change finishes its push cleanup before enable() enable() waits for the cleanup a user change started. When the token was dropped and a different user signs in from enable(), the pushes of the user signed in before end first, and the new user keeps their new opt-in and subscription. A token that sign-out already cleaned up is not cleaned up again when the next user signs in. * fix(puter-js): a user signing back in with the same token gets the user-change cleanup again The token that sign-out already cleaned up is skipped only for the next user change. Once the same token is set again, a later sign-in by a different user ends its pushes as usual. |
||
|
|
96d91cd74e |
fix(puter-js): push syncs skip sign-in and follow disable() across tabs (#4470)
* fix(puter-js): background push syncs never prompt for sign-in The load-time sync and the resync after the browser replaces a subscription ask for the key and announce the subscription without opening Puter's sign-in; only enable() may. The confirmation and the removal never open it, and looking up the user for the opt-in uses a non-interactive /whoami. A rejected token during a background sync is not reported as a sync failure. * fix(puter-js): turning push off in one tab stops other tabs' syncs Tabs of a site take a shared Web Lock to subscribe and announce, and again to confirm, without holding it while the confirmation push is on its way. A load sync re-reads the opt-in inside the lock and stops when it is gone. disable() tells the other tabs first, so their syncs and any enable() in flight stop, then forgets the opt-ins and removes the subscription inside the same lock. Without Web Locks the re-reads and the message still apply. * fix(puter-js): wait for a push confirmation only once the lock is held A sync queued behind another tab's lock started its 30 second confirmation timer and listener before it got the lock, so a long wait failed the sync or left the timeout unhandled. The listener now starts inside the locked step, right before the subscription is announced. * test(puter-js): reach the push sync in the boot sign-in e2e The push page now has the user's opt-in and cached user from an earlier load, starts its sync before boot's own requests come back, and gets a rejected token from the push routes, so the spec covers the key request that must not open a sign-in. * test(puter-js): run the push boot test on full Chromium and skip it without push support The headless shell keeps Notification.permission at 'denied' after the permission is granted, so onPush() never synced there and the test saw no key request. The test now launches full Chromium itself, since a channel can't be set per test, and is skipped on WebKit and wherever the page has no PushManager or notification permission. * test(puter-js): keep the push boot test comment to one line |
||
|
|
cdb3110857 |
fix(ui): honor the default font in showFontPicker on the desktop (#4340)
Backend Tests / changes (push) Canceled after 0s
Backend Tests / typecheck (push) Canceled after 0s
Backend Tests / test shard 1/4 (push) Canceled after 0s
Backend Tests / test shard 2/4 (push) Canceled after 0s
Backend Tests / test shard 3/4 (push) Canceled after 0s
Backend Tests / test shard 4/4 (push) Canceled after 0s
Backend Tests / test (main) (push) Canceled after 0s
Backend Tests / coverage (push) Canceled after 0s
release-please / release-please (push) Canceled after 0s
Notify HeyPuter / notify (push) Canceled after 0s
Maintain Release Merge PR / update-release-pr (push) Canceled after 0s
* fix(ui): honor the default font in showFontPicker on the desktop
String arguments are normalized to { defaultFont } in the IPC handler,
matching showColorPicker, instead of throwing on property assignment.
UIWindowFontPicker reads defaultValue/defaultFont/default case-
insensitively, and the SDK fallback and docs accept the same aliases.
* fix(ui): document the showFontPicker cancel result and alias order
The JSDoc gains overloads for the string and options call forms and
states that closing the picker resolves false in an app and null on a
website; the docs page and examples match. e2e coverage adds a
scroll-into-view check for the preselected font and a close-without-
selecting case.
|
||
|
|
d934a47c5e |
feat: jobs and schedules (#4467)
* feat(db): add nowMsExpr, the database clock in epoch milliseconds, and share the retry backoff
* feat(config): type the jobs config as an augmentable IJobsConfig and resolve its namespace in one place
* feat(jobs): add JobScheduler for fleet- and region-wide system jobs
* refactor(jobs): run core's periodic sweeps as system jobs, and publish the generation bumps of reaped subscriptions
* feat(events): seams for schedules run by an extension, with events_disabled where none is installed
* feat(notifications): add notification.send_key, the deferred mailbox write and notify({ sendAt }) through the schedules contract
* feat(puter.js): schedules, notify({ sendAt }) and notifications.cancel(), and treat a 503 events_disabled as final
* docs(events): document schedules, sendAt and cancel(), with recipes
* fix(events): stop resuming a holder's later pages once the credit sweep's signal aborts, and keep its checkpoint
* fix(events): pre-release fixes for jobs, schedule seams and delayed sends
- The delayed-send write is a field on the notifications driver, not a
method, so /drivers/call can't reach it.
- A delayed send checks for its own earlier write by uid, so it takes no
gap lock that every other notification insert waits on.
- The notification send_key index builds in place without locking writes.
- Reaps re-check expiry and suspension on the primary, so a row resumed
since the replica read is kept.
- Revocation settles skip schedule rows they can't settle.
- An empty handler set read from a replica is re-read on the primary.
- One ended notice per settle, not one per page.
- Sweeps that run once per fleet take more batches per run.
- The global usage rate check keeps its 14-minute minimum window.
- shutdown() waits for prepare hooks a drain already started.
* fix(puter.js): quota refusals are final, and schedule error codes match the server
Schedule, subscription and handler quota 429s no longer go through the
generic retry. ScheduleErrorCode lists what the schedule methods throw,
and the notify({ sendAt }) overload has type tests.
* docs(events): schedule and delayed-send accuracy and style pass
Spread and send-time wording match the runner, the per-user runs-at-once
and rate-limited stored notification limits are published, and the new
pages follow the docs writing rules.
* docs(events): add the Change Data at a Set Time recipe
A one-time schedule that changes data when it runs, cancelling and moving
it, and a repeat-a-few-times variant with maxRuns.
* fix(events): delayed sends recheck the app's access when they fire, and a handler set missing the run's handler is re-read on the primary
Also stops job polling at drain start and marks replayable notification sends.
* feat(events): batch handler invocations to the events dispatcher
Invocations that come due within a few milliseconds go out as one
/invoke-batch request (up to events.invokeBatchSize, default 60). The
dispatcher runs a few at a time and streams a started line and an answer line
per call, so a slow handler delays no other call's answer. A call's timeout
runs from its started line, so time queued behind other calls is never the
handler's; queue time comes out of the connection-wait share instead, and a
call still queued past it is never started and fails as Puter's.
A lone call still uses /invoke, and a dispatcher without the batch route gets
single calls for five minutes before the batch route is tried again.
|
||
|
|
58456ae87c |
fix: signup gate order (#4474)
Backend Tests / changes (push) Canceled after 0s
Backend Tests / typecheck (push) Canceled after 0s
Backend Tests / test shard 1/4 (push) Canceled after 0s
Backend Tests / test shard 2/4 (push) Canceled after 0s
Backend Tests / test shard 3/4 (push) Canceled after 0s
Backend Tests / test shard 4/4 (push) Canceled after 0s
Backend Tests / test (main) (push) Canceled after 0s
Backend Tests / coverage (push) Canceled after 0s
Maintain Release Merge PR / update-release-pr (push) Canceled after 0s
Notify HeyPuter / notify (push) Canceled after 0s
release-please / release-please (push) Canceled after 0s
|
||
|
|
abb70c14a6 |
docs: complex fs recipe (PUT-1978) (#4093)
release-please / release-please (push) Canceled after 0s
Backend Tests / changes (push) Canceled after 0s
Backend Tests / typecheck (push) Canceled after 0s
Backend Tests / test shard 1/4 (push) Canceled after 0s
Backend Tests / test shard 2/4 (push) Canceled after 0s
Backend Tests / test shard 3/4 (push) Canceled after 0s
Backend Tests / test shard 4/4 (push) Canceled after 0s
Backend Tests / test (main) (push) Canceled after 0s
Backend Tests / coverage (push) Canceled after 0s
Maintain Release Merge PR / update-release-pr (push) Canceled after 0s
Notify HeyPuter / notify (push) Canceled after 0s
* docs: complex fs recipe (PUT-1978) A drop folder that reacts to files landing in it, using `puter.events` with an `fs:` subject instead of polling `readdir`. One use case per the recipe guidance: watch, ignore your own writes, recover from a gap, stop watching. The gap section works out what is already done from the folder itself rather than a counter, so a dropped delivery costs a listing and never a file. * docs: rework fs recipe into a CSV importer (PUT-1978) Renames the recipe to "Import Files From a Folder" and reframes it around importing CSV files with puter.fs and onLocal(). Watches the app's own inbox folder, imports files moved in from another folder, and catches up on open and on gaps. Drops the event.self check, which skipped every file the user added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refine 2 --------- Co-authored-by: Reynaldi Chernando <reynaldichernando@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
18fd655645 | fix: sec improvements (#4469) | ||
|
|
480fa7b85b |
chore(deps): bump fast-uri from 3.1.8 to 3.1.9 (#4462)
Backend Tests / changes (push) Canceled after 0s
Backend Tests / typecheck (push) Canceled after 0s
Backend Tests / test shard 1/4 (push) Canceled after 0s
Backend Tests / test shard 2/4 (push) Canceled after 0s
Backend Tests / test shard 3/4 (push) Canceled after 0s
Backend Tests / test shard 4/4 (push) Canceled after 0s
Backend Tests / test (main) (push) Canceled after 0s
Backend Tests / coverage (push) Canceled after 0s
release-please / release-please (push) Canceled after 0s
Maintain Release Merge PR / update-release-pr (push) Canceled after 0s
Notify HeyPuter / notify (push) Canceled after 0s
Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.8 to 3.1.9. - [Release notes](https://github.com/fastify/fast-uri/releases) - [Commits](https://github.com/fastify/fast-uri/compare/v3.1.8...v3.1.9) --- updated-dependencies: - dependency-name: fast-uri dependency-version: 3.1.9 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
47b54aacb4 |
chore(deps): bump postal-mime from 3.0.1 to 4.2.0 (#4461)
Bumps [postal-mime](https://github.com/postalsys/postal-mime) from 3.0.1 to 4.2.0. - [Release notes](https://github.com/postalsys/postal-mime/releases) - [Changelog](https://github.com/postalsys/postal-mime/blob/master/CHANGELOG.md) - [Commits](https://github.com/postalsys/postal-mime/compare/v3.0.1...v4.2.0) --- updated-dependencies: - dependency-name: postal-mime dependency-version: 4.2.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
dd3c0dd485 |
chore(deps): bump qs from 6.16.0 to 6.17.1 (#4460)
Bumps [qs](https://github.com/ljharb/qs) from 6.16.0 to 6.17.1. - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](https://github.com/ljharb/qs/compare/v6.16.0...v6.17.1) --- updated-dependencies: - dependency-name: qs dependency-version: 6.17.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
e0689986ed |
chore(deps): bump systeminformation from 5.33.1 to 5.33.19 (#4425)
Bumps [systeminformation](https://github.com/sebhildebrandt/systeminformation) from 5.33.1 to 5.33.19. - [Release notes](https://github.com/sebhildebrandt/systeminformation/releases) - [Changelog](https://github.com/sebhildebrandt/systeminformation/blob/master/CHANGELOG.md) - [Commits](https://github.com/sebhildebrandt/systeminformation/compare/v5.33.1...v5.33.19) --- updated-dependencies: - dependency-name: systeminformation dependency-version: 5.33.19 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
0f54588b31 |
chore(deps): bump qs from 6.16.0 to 6.17.1 in /src/puter-js (#4427)
Bumps [qs](https://github.com/ljharb/qs) from 6.16.0 to 6.17.1. - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](https://github.com/ljharb/qs/compare/v6.16.0...v6.17.1) --- updated-dependencies: - dependency-name: qs dependency-version: 6.17.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
0cb9f7707d |
chore(deps): bump follow-redirects in /src/puter-js (#4424)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.16.1 to 1.16.2. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.16.1...v1.16.2) --- updated-dependencies: - dependency-name: follow-redirects dependency-version: 1.16.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
622b782f4e |
chore(deps): bump follow-redirects from 1.16.1 to 1.16.2 (#4426)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.16.1 to 1.16.2. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.16.1...v1.16.2) --- updated-dependencies: - dependency-name: follow-redirects dependency-version: 1.16.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
efc0ab7126 |
docs: refresh KV sharing, Events and Notifications docs; fix router CORS and KV size error (#4445)
release-please / release-please (push) Canceled after 0s
Backend Tests / changes (push) Canceled after 0s
Backend Tests / typecheck (push) Canceled after 0s
Backend Tests / test shard 1/4 (push) Canceled after 0s
Backend Tests / test shard 2/4 (push) Canceled after 0s
Backend Tests / test shard 3/4 (push) Canceled after 0s
Backend Tests / test shard 4/4 (push) Canceled after 0s
Backend Tests / test (main) (push) Canceled after 0s
Backend Tests / coverage (push) Canceled after 0s
Maintain Release Merge PR / update-release-pr (push) Canceled after 0s
Notify HeyPuter / notify (push) Canceled after 0s
* docs: refresh KV sharing, Events and Notifications docs and recipes Bring the KV, Events and Notifications reference pages and the recipes that use them in line with current behavior, and tighten them. - KV: share levels, re-share and recipient rules, handle scoping, list limits, the 399 KB value limit, and full error tables per method. - Events: kv:<handle> subjects, what each way of losing access does to a subscription, fetch() notification types, handler ownership, and accurate anchor-deletion and deleted-app behavior. - Notifications: where a push goes, desktop and website behavior, sensitive taps, share-notify limits, and enable()/status() errors. - Recipes: fix broken code paths (undefined helpers, single-page share listings, read-modify-write races, oversized batch writes, missing value fallbacks), use events.update(), onlyIfAbsent and batch share recipients where they fit, and cut repetition. - New recipes: Add a Notification Settings Switch, Clean Up Background Work. - Rename push-when-closed to reach-a-user-when-your-app-is-closed, drop playground examples that need an app the reader owns, and redirect both. * fix(workers): send CORS on router 404s and readable 500 bodies Unmatched paths and methods now carry Access-Control-Allow-Origin like every other router response, so a browser sees the 404 instead of a CORS failure. A handler response with immutable headers is copied rather than turned into a 500, and a thrown puter.js error object is sent as JSON. * fix(kv): one error code for oversized values, and SDK types that match the store The store now rejects a value over 399 KB with value_too_large, as the SDK's own check and a growing path write already did. get() declares its batch form, and the JSDoc gives the 399 KB limit and says watch shares can list keys. * docs(notifications): notifications sent within 250 ms reach live listeners as one * test(events): give the per-handle subscription cap test room on a loaded runner It fills the whole cap of 1,000 session subscriptions one add at a time, which went past vitest's 5 s default on a busy CI shard.26.11 |
||
|
|
ef99893fc8 | fix(notifications): a browser removed in Puter stays off, and status() checks with Puter on websites (#4458) |