Skip to content

Release Next v3.0.0-next.17

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 27 Oct 11:42
· 262 commits to main since this release
07aa3f2

@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 carries archivedAt. shares.list leaves archived links out unless asked with archived=true. shares.delete deletes only a link whose visitors made no changes and answers 409 otherwise, and shares.update answers 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 (expiresIn 60 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}, scope docs.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, scope docs.read): a document's layers that hold changes, newest first, paged like the other tenant lists with limit, cursor and nextCursor. Each record says whose layer it is (shareId and visitorId for a share-link visitor's own), its storageSizeBytes, and when it was made, last changed and last seen. shareId filters 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 visitorIdleSeconds is 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 on shares.update to 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's visitor.identityRequest (ShareVisitorIdentityRequestSchema) says what a link that asks before the first change still needs. New operations shares.visitors.list and shares.visitors.delete, with ShareVisitorRecordSchema. Admin error payloads may carry error.details. visitorLayerIdleSeconds is now visitorIdleSeconds.

  • #902 by @bobsingor – Share links say whose layer their visitors write into. layerMode is 'shared' (one layer, layerName, as before) or 'per-visitor' (each visitor gets a layer of their own, and layerName is null), with keepVisitorLayers and visitorIdleSeconds (an hour to 30 days) for per-visitor links. shares.create and shares.update take all of them, so a link can switch modes and keep its token. The exchange takes visitorHandle and restart, and answers layerMode and visitor: { handle, keep }: send the handle back to stay the same visitor, and keep it for the next visit when keep is 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 visitorStore for the next open: the browser's localStorage by 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, and doc.share says 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 with Forbidden (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 with VisitorIdentityRequired, whose details are 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 once doc.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 same opId. A passphrase or answer the exchange refuses keeps its details on the EngineError.

@cloudpdf/sdk@3.0.0-next.17

Minor Changes

  • #902 by @bobsingor – Share links that ask their visitors who they are: visitorIdentity on client.shares.create and client.shares.update, the visitor's answers on client.shares.exchange, and visitor.identityRequest in its response. client.shares.visitors.list lists a link's visitors with their answers and layer, and client.shares.visitors.delete erases one. visitorLayerIdleSeconds is now visitorIdleSeconds.

@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 of shares.list unless archived=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-link returns 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 a link token 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 after expiresIn, 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 with layer_name and a download claim.

  • #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 with LayerDeleted (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 visitorIdentity names 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's purpose and privacyUrl. The exchange takes the answers as visitorIdentity; until the required ones are given, a link that asks on open answers 422 VisitorIdentityRequired, and one that asks before the first change issues the session with visitor.identityRequest and refuses its writes with 403 VisitorIdentityRequired. 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 its identity; 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}/visitors lists a link's visitors (who answered or made a change) newest first with their layer, and DELETE …/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 from visitorLayerIdleSeconds) 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.userId visitor_<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: true deletes the visitor's layer and starts a new visitor, and a visitor whose layer was deleted comes back as a new one. Names starting with visitor- 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 visitorIdleSeconds is 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 /access hands 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 most maxLayerSessionsPerDoc layer 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, immutable instead of public, …. 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 keep public, 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), and documents.retry(id, { sharePassword }) or retry(id, { visitor }) opens it with them; answers add to the ones given before. Once open, a change waiting for the visitor's answers shows as DocumentInfo.identityRequest until documents.identify(id, answers) sends it or documents.declineIdentity(id) lets it fail.

Patch Changes

  • #902 by @bobsingor – A LayerDeleted refusal from the engine reaches plugins as not-found.

@embedpdf/engine-core@3.0.0-next.17

Minor Changes

  • #902 by @bobsingor – Add wirePaths.layerDownloadLink and its template, the route that makes a short-lived download URL for a layer's PDF.

  • #902 by @bobsingor – Add the LayerDeleted error code: the cloud layer was deleted, what it held is gone, and its name is refused from then on.

  • #902 by @bobsingor – OpenInputShare.restart starts over on a share link that keeps each visitor's layer, and DocumentHandle.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 its details (VisitorIdentityRequest: fields, missing, invalid, purpose, privacyUrl) say what; visitorIdentityRequestOf(error) reads them. OpenInputShare.visitor sends the visitor's answers (VisitorAnswers) with the open. DocumentHandle.share is now a DocumentShare: besides layerMode and keep, identityRequest says what the link asks before the first change, identify(answers) answers it, onIdentityRequest is called when a change waits for the answers, and decline() 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, what annotationState reads and useAnnotationState() 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 /internal and /contract/host, is now AnnotationModel, made by initialAnnotationModel(), so AnnotationState has one meaning.

@embedpdf/plugin-attachment@3.0.0-next.17

Minor Changes

  • #902 by @bobsingor – A named type for the state: AttachmentState, what attachmentState reads and useAttachmentState() 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, what formState reads and useFormState() 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 now FormModel, made by initialFormModel(), so FormState has one meaning.

@embedpdf/plugin-history@3.0.0-next.17

Minor Changes

  • #902 by @bobsingor – A named type for the state: HistoryState, what historyState reads and useHistoryState() 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, what i18nState reads and useI18nState() 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, what interactionState reads and useInteractionState() 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, what measurementState reads and useMeasurementState() 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 /internal and /contract/host, is now MeasurementModel, made by initialMeasurementModel(), so MeasurementState has one meaning.

@embedpdf/plugin-metadata@3.0.0-next.17

Minor Changes

  • #902 by @bobsingor – A named type for the state: MetadataState, what metadataState reads and useMetadataState() 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, what outlineState reads and useOutlineState() 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, what pageEditState reads and usePageEditState() 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, what redactionState reads and useRedactionState() 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 /internal and /contract/host, is now RedactionModel, made by initialRedactionModel(), so RedactionState has one meaning.

@embedpdf/plugin-search@3.0.0-next.17

Minor Changes

  • #902 by @bobsingor – A named type for the state: SearchState, what searchState reads and useSearchState() 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, what selectionState reads and useSelectionState() 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 now SelectionModel, made by initialSelectionModel(), so SelectionState has one meaning.

@embedpdf/plugin-shell@3.0.0-next.17

Minor Changes

  • #902 by @bobsingor – A named type for the state: ShellState, what shellState reads and useShellState() 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 /internal and /contract/host, is now ShellModel, made by initialShellModel(), so ShellState has one meaning.

@embedpdf/plugin-signature@3.0.0-next.17

Minor Changes

  • #902 by @bobsingor – A named type for the state: SignatureState, what signatureState reads and useSignatureState() 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 /internal and /contract/host, is now SignatureModel, made by initialSignatureModel(), so SignatureState has one meaning.

@embedpdf/plugin-stage@3.0.0-next.17

Minor Changes

  • #902 by @bobsingor – A named type for the state: StageState, what stageState reads and useStageState() 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 now StageModel, made by initialStageModel(), so StageState has one meaning.

@embedpdf/plugin-stamp@3.0.0-next.17

Minor Changes

  • #902 by @bobsingor – A named type for the state: StampState, what stampState reads and useStampState() 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 /internal and /contract/host, is now StampModel, made by initialStampModel(), so StampState has one meaning.

@embedpdf/plugin-view-manager@3.0.0-next.17

Minor Changes

  • #902 by @bobsingor – A named type for the state: ViewManagerState, what viewManagerState reads and useViewManagerState() 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 /internal and /contract/host, is now ViewManagerModel, made by initialViewManagerModel(), so ViewManagerState has 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: dist keeps the #quickjs-variant import, so a browser bundler resolves QuickJS's browser build and Node its wasm-file build. Before, the build fixed the wasm-file build into dist for 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() and useSignatureState() 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() and useSignatureState() are typed like every other state composable, through the plugins' named state types (StateComposable<RedactionState>).

@cloudpdf/viewer-angular@3.0.0-next.17

@cloudpdf/viewer-react@3.0.0-next.17

@cloudpdf/viewer-svelte@3.0.0-next.17

@cloudpdf/viewer-vue@3.0.0-next.17

@embedpdf/core-acrojs@3.0.0-next.17

@embedpdf/core-annotation@3.0.0-next.17

@embedpdf/core-fonts@3.0.0-next.17

@embedpdf/core-form@3.0.0-next.17

@embedpdf/core-geometry@3.0.0-next.17

@embedpdf/core-signature@3.0.0-next.17

@embedpdf/core-stage@3.0.0-next.17

@embedpdf/core-ui@3.0.0-next.17

@embedpdf/engine@3.0.0-next.17

@embedpdf/engine-runtime@3.0.0-next.17

@embedpdf/engine-runtime-darwin-arm64@3.0.0-next.17

@embedpdf/engine-runtime-darwin-x64@3.0.0-next.17

@embedpdf/engine-runtime-linux-arm64@3.0.0-next.17

@embedpdf/engine-runtime-linux-x64@3.0.0-next.17

@embedpdf/engine-runtime-linuxmusl-arm64@3.0.0-next.17

@embedpdf/engine-runtime-linuxmusl-x64@3.0.0-next.17

@embedpdf/engine-runtime-wasm32@3.0.0-next.17

@embedpdf/engine-runtime-win32-arm64@3.0.0-next.17

@embedpdf/engine-runtime-win32-x64@3.0.0-next.17

@embedpdf/engine-services@3.0.0-next.17

@embedpdf/angular@3.0.0-next.17

@embedpdf/web@3.0.0-next.17

@embedpdf/plugin-actions@3.0.0-next.17

@embedpdf/plugin-commands@3.0.0-next.17

@embedpdf/plugin-link@3.0.0-next.17

@embedpdf/plugin-render@3.0.0-next.17

@embedpdf/viewer-angular@3.0.0-next.17

@embedpdf/viewer@3.0.0-next.17

@embedpdf/viewer-react@3.0.0-next.17

@embedpdf/viewer-svelte@3.0.0-next.17

@embedpdf/viewer-vue@3.0.0-next.17