Repository navigation
Release Next v3.0.0-next.17
Pre-release@cloudpdf/contract@3.0.0-next.17
Minor Changes
-
#902 by @bobsingor – Share links can be archived:
shares.archive(POST /v1/tenants/{tenantId}/shares/{shareId}/archive) ends a link for good while keeping what its visitors made, and the record carriesarchivedAt.shares.listleaves archived links out unless asked witharchived=true.shares.deletedeletes only a link whose visitors made no changes and answers 409 otherwise, andshares.updateanswers 409 for an archived link. -
#902 by @bobsingor – Add
doc.downloadLink(POST /v1/docs/{docId}/layers/{layerName}/download-link,doc.download): a short-lived URL (expiresIn60 to 3600 seconds, default 300) that downloads the layer's current PDF with a plain GET and no Authorization header, for services that only take a URL. It downloads that one file as it is when made, and answers 404 once the layer changes. -
#902 by @bobsingor – Add
documents.layers.delete(DELETE /v1/tenants/{tenantId}/documents/{id}/layers/{layerName}, scopedocs.delete): delete a layer and everything it stored, for example a user's data. A layer that was written can't be used again under its name; one nobody wrote stays usable. A layer a share link uses is refused with 409 until the link is deleted. -
#902 by @bobsingor – Add
documents.layers.list(GET /v1/tenants/{tenantId}/documents/{id}/layers, scopedocs.read): a document's layers that hold changes, newest first, paged like the other tenant lists withlimit,cursorandnextCursor. Each record says whose layer it is (shareIdandvisitorIdfor a share-link visitor's own), itsstorageSizeBytes, and when it was made, last changed and last seen.shareIdfilters to one link's visitors, a deleted link's too. A layer appears at its first change, and a deleted one is not listed. -
#902 by @bobsingor – A share link's
visitorIdleSecondsis nullable and null by default: visitors' layers stay until they are deleted on purpose. Set it (an hour to 30 days) only for links whose visitors' layers are throwaway, and set it back to null onshares.updateto stop deleting them. -
#902 by @bobsingor – Share links take
visitorIdentity(ShareVisitorIdentitySchema: the fields to ask, each required or optional,ask,purpose,privacyUrl), and the exchange request takes the visitor's answers (ShareVisitorAnswersSchema). The exchange response'svisitor.identityRequest(ShareVisitorIdentityRequestSchema) says what a link that asks before the first change still needs. New operationsshares.visitors.listandshares.visitors.delete, withShareVisitorRecordSchema. Admin error payloads may carryerror.details.visitorLayerIdleSecondsis nowvisitorIdleSeconds. -
#902 by @bobsingor – Share links say whose layer their visitors write into.
layerModeis'shared'(one layer,layerName, as before) or'per-visitor'(each visitor gets a layer of their own, andlayerNameisnull), withkeepVisitorLayersandvisitorIdleSeconds(an hour to 30 days) for per-visitor links.shares.createandshares.updatetake all of them, so a link can switch modes and keep its token. The exchange takesvisitorHandleandrestart, and answerslayerModeandvisitor: { handle, keep }: send the handle back to stay the same visitor, and keep it for the next visit whenkeepis true.
@cloudpdf/engine@3.0.0-next.17
Minor Changes
-
#902 by @bobsingor – Share links keep their visitor. Every renewal sends the handle the last exchange answered, so the visitor and, on a per-visitor link, their layer stay the same; an open document also renews on a timer, so a visitor who only reads still counts as seen. When the link keeps visitors' layers, the handle waits in the engine's
visitorStorefor the next open: the browser'slocalStorageby default, none outside a browser (browserVisitorStore(), or a store of your own).open({ kind: 'share', shareToken, restart: true })deletes the kept layer and starts a new visitor, anddoc.sharesays whether the layer is shared or the visitor's own and whether it is kept. An open whose layer was deleted under it (another tab started over) refuses withForbidden(details.reason: 'layer-changed'); opening again picks up the new layer. -
#902 by @bobsingor – Share links that ask who the visitor is.
open({ kind: 'share', shareToken, visitor })sends the answers; without the required ones, a link that asks on open fails the open withVisitorIdentityRequired, whosedetailsare the questions. A link that asks before the first change opens at once: its changes wait in the document's write line while something listens (doc.share.onIdentityRequest), and go out as they were oncedoc.share.identify(answers)succeeds;doc.share.decline()fails them, and with nothing listening they fail at once. A change the server refuses for the answers waits the same way and is sent again with the sameopId. A passphrase or answer the exchange refuses keeps its details on theEngineError.
@cloudpdf/sdk@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – Share links that ask their visitors who they are:
visitorIdentityonclient.shares.createandclient.shares.update, the visitor's answers onclient.shares.exchange, andvisitor.identityRequestin its response.client.shares.visitors.listlists a link's visitors with their answers and layer, andclient.shares.visitors.deleteerases one.visitorLayerIdleSecondsis nowvisitorIdleSeconds.
@cloudpdf/server@3.0.0-next.17
Minor Changes
-
#902 by @bobsingor – Archive share links instead of losing what their visitors made. Archiving (migration 041) stops a link's exchange for good, like a revoke, but keeps the link and its visitors' layers, still listed with
documents.layers.list?shareId=and downloadable. Deleting a link is refused with 409 while its visitors have layers; one whose visitors only looked can still be deleted. An archived link refuses updates, is left out ofshares.listunlessarchived=true, and no longer keeps its shared layer from being deleted. Its visitors' layers keep to the link's idle setting. -
#902 by @bobsingor – Incremental downloads no longer go through the engine. A layer's PDF saved incrementally, the default, is now its base file followed by the delta its stored artifact holds, streamed from storage with the same permission and password checks as before; a layer nobody wrote downloads as its base file, and a signed file's bytes stay exactly as stored. A rewrite (
mode=rewrite) is still saved by the engine.Download links:
POST …/layers/{layerName}/download-linkreturns a URL that downloads the layer's current PDF without an Authorization header, for services that only take a plain URL. The URL is the layer's versioned download with alinktoken that opens nothing else: it isn't accepted as a bearer token, it works only for the version it was made for (404 once the layer changes), it expires afterexpiresIn, and a password-protected document is checked when the link is made. Deployments that verify tokens with their own keys can mint the same link on their backend, as a doc token withlayer_nameand adownloadclaim. -
#902 by @bobsingor – Delete a layer with
DELETE /v1/tenants/{tenantId}/documents/{id}/layers/{layerName}. Its artifacts, renders, change captures, changes, audit rows, signings, password sessions and object numbers go, and its worker session closes. A layer that was written keeps its row as a tombstone (migration 037), and every route then answers its name withLayerDeleted(HTTP 410), because its read URLs were cached as immutable and a new layer under the name would restart at the same versions. A layer nobody wrote leaves no tombstone and stays usable. A write in flight on another replica loses its commit to the delete. Deleting a layer a share link uses is refused with 409, and a share link can't be created for a deleted layer. -
#902 by @bobsingor – List a document's layers with
GET /v1/tenants/{tenantId}/documents/{id}/layers: newest first, cursor-paged like documents, tenants and shares, optionally only one share link's visitors' layers (shareId). This is how a link's owner finds what visitors filled in and downloads it through the layer's routes. Migration 040 replaces the per-document layer index with one in list order. -
#902 by @bobsingor – Share links can ask their visitors who they are. A link's
visitorIdentitynames the fields it asks (displayName,email,title,organization,organizationalUnit, each required or optional), when it asks ('on-open'or'before-first-change'), and the owner'spurposeandprivacyUrl. The exchange takes the answers asvisitorIdentity; until the required ones are given, a link that asks on open answers 422VisitorIdentityRequired, and one that asks before the first change issues the session withvisitor.identityRequestand refuses its writes with 403VisitorIdentityRequired. Answers are stored once per visitor (migration 042,share_visitors), with the time they were given, and go into every token issued for them as itsidentity; an answer once given is never replaced, and a visitor who starts over is a new visitor who is asked again.GET /v1/tenants/{tenantId}/shares/{shareId}/visitorslists a link's visitors (who answered or made a change) newest first with their layer, andDELETE …/visitors/{visitorId}erases a visitor's answers and layer, on archived links too. A link with visitors is archived rather than deleted (409). The idle clock is now the visitor's:visitorIdleSeconds(renamed fromvisitorLayerIdleSeconds) deletes the visitor and their layer once unseen that long, only on links that set it. -
#902 by @bobsingor – Share links can give each visitor a layer of their own. Every exchange names a visitor: the session token carries them as the author (
identity.userIdvisitor_<id>), and a handle, an HMAC the link issued, brings them back on renewal. On a per-visitor link the session pins the visitor's layer,visitor-<id>, which is made at their first write and stamped with the link, the visitor and when they were last seen (migration 038); each exchange with the handle restarts that idle clock.restart: truedeletes the visitor's layer and starts a new visitor, and a visitor whose layer was deleted comes back as a new one. Names starting withvisitor-are refused for a link's shared layer and for issued tokens. -
#902 by @bobsingor – Deleting share-link visitors' layers is now the link owner's choice. A link's
visitorIdleSecondsis null by default, and a visitor's layer then stays until it is deleted on purpose, since what a visitor filled in is often the only copy; migration 039 moves links on the earlier 7-day default to it. A link that sets an idle time, for throwaway layers like examples, has the sweeper delete its visitors' layers nobody has seen for that long; a visitor with the document open renews and stays seen. A visit through a share link now counts as one view however often its session renews: the visitor handle carries the signed time it was issued, and an exchange whose handle is newer than the session lifetime plus five minutes continues the visit.
Patch Changes
-
#902 by @bobsingor – Opening a document to edit no longer creates its layer. A layer nobody has changed reads the base document, so
/accesshands its editing session object numbers counted past the base document's last object, without storing the layer or opening a worker session for it. The layer is stored at its first write. Object numbers are now kept by document and layer name (migration 036), and numbers a layer was handed on one base version start over on another. The sweeper forgets the numbers of layers nobody changed once their editing sessions have been expired for a session lifetime, so someone who only looked leaves nothing behind.Layer worker sessions no longer stay open for the life of the document. The sweeper closes a layer's session after it goes unused for
layerSessionIdleMs(default 10 minutes), and a document keeps at mostmaxLayerSessionsPerDoclayer sessions open (default 32), closing the least recently used beyond that. The next request reopens a closed session from the stored layer. Searching a layer whose text and page order are still the base document's now runs on the base, without opening a session for the layer.A write retried on two replicas under the same change id now answers what the first one committed, also when that commit spent the write's object numbers while the second was still preparing.
-
#902 by @bobsingor – Versioned reads without a CDN now answer
Cache-Control: private, max-age=31536000, immutableinstead ofpublic, …. Only the caller's browser may keep a response that needed a token; a proxy in front of the engine no longer may. Deployments with a CDN keeppublic, because the edge checks the signature before serving its copy.
@cloudpdf/viewer@3.0.0-next.17
Minor Changes
-
#902 by @bobsingor – A share link source takes
restart(src: { shareToken, restart: true }) to start over on a link that keeps each visitor's layer. -
#902 by @bobsingor –
src: { shareToken, visitor }passes the visitor's answers to what a share link asks, when your page already has them. Without them, the viewer asks.
@embedpdf/core@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – Documents from share links that wait for something. A failed open says what its share link waits for in
DocumentInfo.shareRequest(the passphrase, with whether one was tried, or the visitor's answers), anddocuments.retry(id, { sharePassword })orretry(id, { visitor })opens it with them; answers add to the ones given before. Once open, a change waiting for the visitor's answers shows asDocumentInfo.identityRequestuntildocuments.identify(id, answers)sends it ordocuments.declineIdentity(id)lets it fail.
Patch Changes
- #902 by @bobsingor – A
LayerDeletedrefusal from the engine reaches plugins asnot-found.
@embedpdf/engine-core@3.0.0-next.17
Minor Changes
-
#902 by @bobsingor – Add
wirePaths.layerDownloadLinkand its template, the route that makes a short-lived download URL for a layer's PDF. -
#902 by @bobsingor – Add the
LayerDeletederror code: the cloud layer was deleted, what it held is gone, and its name is refused from then on. -
#902 by @bobsingor –
OpenInputShare.restartstarts over on a share link that keeps each visitor's layer, andDocumentHandle.share(layerMode,keep) says how a document opened from a share link reaches its layer. -
#902 by @bobsingor – New engine code
VisitorIdentityRequired: a share link asks who the visitor is, and itsdetails(VisitorIdentityRequest:fields,missing,invalid,purpose,privacyUrl) say what;visitorIdentityRequestOf(error)reads them.OpenInputShare.visitorsends the visitor's answers (VisitorAnswers) with the open.DocumentHandle.shareis now aDocumentShare: besideslayerModeandkeep,identityRequestsays what the link asks before the first change,identify(answers)answers it,onIdentityRequestis called when a change waits for the answers, anddecline()lets the waiting changes fail.
@embedpdf/plugin-annotation@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – A named type for the state:
AnnotationState, whatannotationStatereads anduseAnnotationState()returns, the State table of the selecting page with a comment on each field. Published types now name it instead of spelling out its fields. The plugin's model, from/internaland/contract/host, is nowAnnotationModel, made byinitialAnnotationModel(), soAnnotationStatehas one meaning.
@embedpdf/plugin-attachment@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – A named type for the state:
AttachmentState, whatattachmentStatereads anduseAttachmentState()returns, the State table of the attachment page with a comment on each field. Published types now name it instead of spelling out its fields.
@embedpdf/plugin-form@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – A named type for the state:
FormState, whatformStatereads anduseFormState()returns, the State table of the form page with a comment on each field. Published types now name it instead of spelling out its fields. The plugin's model, from/internal, is nowFormModel, made byinitialFormModel(), soFormStatehas one meaning.
@embedpdf/plugin-history@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – A named type for the state:
HistoryState, whathistoryStatereads anduseHistoryState()returns, the State table of the history page with a comment on each field. Published types now name it instead of spelling out its fields.
@embedpdf/plugin-i18n@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – A named type for the state:
I18nState, whati18nStatereads anduseI18nState()returns, the State table of the translations page with a comment on each field. Published types now name it instead of spelling out its fields.
@embedpdf/plugin-interaction@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – A named type for the state:
InteractionState, whatinteractionStatereads anduseInteractionState()returns, the State table of the tools page with a comment on each field. Published types now name it instead of spelling out its fields.
@embedpdf/plugin-measurement@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – A named type for the state:
MeasurementState, whatmeasurementStatereads anduseMeasurementState()returns, the State table of the measurements page with a comment on each field. Published types now name it instead of spelling out its fields. The plugin's model, from/internaland/contract/host, is nowMeasurementModel, made byinitialMeasurementModel(), soMeasurementStatehas one meaning.
@embedpdf/plugin-metadata@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – A named type for the state:
MetadataState, whatmetadataStatereads anduseMetadataState()returns, the State table of the metadata page with a comment on each field. Published types now name it instead of spelling out its fields.
@embedpdf/plugin-outline@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – A named type for the state:
OutlineState, whatoutlineStatereads anduseOutlineState()returns, the State table of the outline page with a comment on each field. Published types now name it instead of spelling out its fields.
@embedpdf/plugin-page-edit@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – A named type for the state:
PageEditState, whatpageEditStatereads andusePageEditState()returns, the State table of the pages page with a comment on each field. Published types now name it instead of spelling out its fields.
@embedpdf/plugin-redaction@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – A named type for the state:
RedactionState, whatredactionStatereads anduseRedactionState()returns, the State table of the redaction page with a comment on each field. Published types now name it instead of spelling out its fields. The plugin's model, from/internaland/contract/host, is nowRedactionModel, made byinitialRedactionModel(), soRedactionStatehas one meaning.
@embedpdf/plugin-search@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – A named type for the state:
SearchState, whatsearchStatereads anduseSearchState()returns, the State table of the search page with a comment on each field. Published types now name it instead of spelling out its fields.
@embedpdf/plugin-selection@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – A named type for the state:
SelectionState, whatselectionStatereads anduseSelectionState()returns, the State table of the selection page with a comment on each field. Published types now name it instead of spelling out its fields. The plugin's model, from/internal, is nowSelectionModel, made byinitialSelectionModel(), soSelectionStatehas one meaning.
@embedpdf/plugin-shell@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – A named type for the state:
ShellState, whatshellStatereads anduseShellState()returns, the State table of the panels page with a comment on each field. Published types now name it instead of spelling out its fields. The plugin's model, from/internaland/contract/host, is nowShellModel, made byinitialShellModel(), soShellStatehas one meaning.
@embedpdf/plugin-signature@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – A named type for the state:
SignatureState, whatsignatureStatereads anduseSignatureState()returns, the State table of the signatures page with a comment on each field. Published types now name it instead of spelling out its fields. The plugin's model, from/internaland/contract/host, is nowSignatureModel, made byinitialSignatureModel(), soSignatureStatehas one meaning.
@embedpdf/plugin-stage@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – A named type for the state:
StageState, whatstageStatereads anduseStageState()returns, the State table of the Stage page with a comment on each field. Published types now name it instead of spelling out its fields. The plugin's model, from/contract/host, is nowStageModel, made byinitialStageModel(), soStageStatehas one meaning.
@embedpdf/plugin-stamp@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – A named type for the state:
StampState, whatstampStatereads anduseStampState()returns, the State table of the stamps page with a comment on each field. Published types now name it instead of spelling out its fields. The plugin's model, from/internaland/contract/host, is nowStampModel, made byinitialStampModel(), soStampStatehas one meaning.
@embedpdf/plugin-view-manager@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – A named type for the state:
ViewManagerState, whatviewManagerStatereads anduseViewManagerState()returns, the State table of the Several documents page with a comment on each field. Published types now name it instead of spelling out its fields. The plugin's model, from/internaland/contract/host, is nowViewManagerModel, made byinitialViewManagerModel(), soViewManagerStatehas one meaning.
@embedpdf/viewer-chrome@3.0.0-next.17
Minor Changes
- #902 by @bobsingor – The viewer asks for what a share link waits for: its passphrase, or who the visitor is, in the document's tab before it opens, and in a dialog when a change waits for the visitor's answers. The questions show the link owner's purpose and privacy notice, say that the visitor's name shows on what they add, and that the answers can't be changed later. A tab waiting for a passphrase or answers shows a lock. Translated in every language the viewer ships.
@embedpdf/core-js-sandbox@3.0.0-next.17
Patch Changes
- #902 by @bobsingor – The published package now picks its QuickJS build where it runs:
distkeeps the#quickjs-variantimport, so a browser bundler resolves QuickJS's browser build and Node its wasm-file build. Before, the build fixed the wasm-file build intodistfor both.
@embedpdf/react@3.0.0-next.17
Patch Changes
- #902 by @bobsingor – The state hooks' published types name each plugin's state type (
useFormState: StateHook<FormState>) instead of spelling out its fields. The declarations no longer carry a copy of engine types, which they did on some builds, from 34 kB to 229 kB with zod's types inside.
@embedpdf/svelte@3.0.0-next.17
Patch Changes
- #902 by @bobsingor –
useRedactionState()anduseSignatureState()are typed like every other state reader, through the plugins' named state types (StateReader<RedactionState>).
@embedpdf/vue@3.0.0-next.17
Patch Changes
- #902 by @bobsingor –
useRedactionState()anduseSignatureState()are typed like every other state composable, through the plugins' named state types (StateComposable<RedactionState>).