Repository navigation
Release Next v3.0.0-next.18
Pre-release@cloudpdf/contract@3.0.0-next.18
Minor Changes
-
#917 by @bobsingor – Add folders:
folders.create,folders.list,folders.get(with the path
above the folder),folders.updateto rename,folders.moveIntoto move
documents and folders together, andfolders.deletefor empty folders. The
top level is the placeroot. Document records carryfolderId;
documents.initanddocuments.importFromtake one, anddocuments.list
filters by it. Folder conflicts answer 409 withdetails.reason. -
#917 by @bobsingor – Documents have a
name: set it withdocuments.initordocuments.import
(it defaults to the document id) and change it with the newdocuments.update
operation, which needs the newdocs.updatetenant scope.documents.list
gainsqto search names ignoring case,sort(createdAt,nameor
size) andorder, and no longer lists documents that are being deleted
unlessstate=deletingasks for them. -
#917 by @bobsingor –
folders.deletetakesrecursive=trueto delete a folder with everything in
it. Without it, a folder that holds anything is still refused. -
#917 by @bobsingor – Document operations accept tenant JWTs, which need the operation's document
capabilities exactly as a document JWT does; no tenant scope reaches inside a
document. A tenant token'sscopenow takes document capabilities next to
tenant scopes, and they hold for every document of the tenant.
documents.downloadneedsdoc.downloadanddocuments.thumbnailneeds
doc.renderinstead ofdocs.read.docs.updaterenames and moves, and never
changes content.
Patch Changes
-
#910 by @bobsingor – The
doc.versions.*operations take the layer: their paths are/v1/docs/:docId/layers/:layerName/versions….doc.versions.listanswersLayerVersions({ baseSha, versions }): the layer's base and its versions, the upload first. -
#909 by @bobsingor – Share links carry
identity(who their visitors are, for a link made for one person) on the link record,shares.createandshares.update.Identityis a named component, shared withtokens.issue, so the SDKs have one type for it. -
#910 by @bobsingor – The signature notes describe the new rule. A signature is made over the layer's own base, and its new version is that layer's own: the document and every other layer stay as they were.
StaleBaseis no longer an error code.
@cloudpdf/sdk@3.0.0-next.18
Minor Changes
-
#917 by @bobsingor – Add the
foldersclient (create,list,get,update,moveInto,
delete), afolderIdoption ondocuments.init,documents.importFrom,
documents.listanduploads.create, andfolderIdon document records. -
#917 by @bobsingor – Add
documents.updatefor renaming documents, anameoption on
documents.init,documents.importFromanduploads.create, and theq,
sortandorderoptions ondocuments.list. Document records now carry
name. -
#917 by @bobsingor – Add the
recursiveoption tofolders.delete. -
#910 by @bobsingor –
doc.versions.*takes the layer:list,signatures,signatureContents,signatureDigest,analysis,downloadandrevisionread/v1/docs/{docId}/layers/{layerName}/versions….listanswers the layer'sbaseShaand its versions (the upload, then the ones its signatures made), and another layer's version is 404. -
#909 by @bobsingor –
shares.createandshares.updatetakeidentity, and a share record returns it: who a link's visitors are, for a link made for one person.Identityis one shared type, used bytokens.issuetoo. -
#917 by @bobsingor – A tenant token's
scopeintokens.issuetakes document capabilities next to
tenant scopes, and the document operations document tenant tokens as a
credential.
Patch Changes
- #910 by @bobsingor –
StaleBaseis no longer an engine error code, and the signature notes describe the new rule: a signature's new version belongs to its layer, and the document and every other layer stay as they were.
@cloudpdf/server@3.0.0-next.18
Minor Changes
-
#917 by @bobsingor – Organize documents in folders. Folders nest up to 100 levels with names
unique among siblings, ignoring case. A move takes documents and folders
together or not at all and never makes a loop, even when two moves race;
only empty folders delete. Documents are filed at upload and listed per
folder, and a document being deleted leaves its folder. Migration 046 adds
thefolderstable anddocuments.folder_id. Errors thrown with
structured context now return it asdetails. -
#917 by @bobsingor – Store a name for every document, searchable and sortable without regard to
case, and rename documents withPATCH /v1/tenants/:tenantId/documents/:id
under the newdocs.updatescope. The documents list searches by name, sorts
by creation time, name or size in either order with stable cursors, and hides
documents that are being deleted unless asked for by state. Migration 045 names
existing documents frommetadata.namewhen it is a non-empty string, otherwise
from their id. -
#917 by @bobsingor – Delete a folder with everything in it via
recursive=true: the folder and
every folder under it go in one transaction, and every document in them stops
opening, leaves every list and stops answering its share links at once, while
their stored files are removed in the background. The sweeper now also
finishes deletes left behind, such as one interrupted by a crash, after
deletingGraceMs(default one minute). The filesystem object store no longer
fails a write when a delete elsewhere in the same tenant prunes the
directories the write has just made. -
#917 by @bobsingor – Tenant scopes act on records and document capabilities act on what is inside
documents, on any token. A tenant JWT is checked inside documents the way a
document JWT is, against the document capabilities its scope names, which hold
for every document of the tenant;docs.readlists documents but no longer
opens, reads or changes them. Downloading the original file needs
doc.downloadand the thumbnail needsdoc.render. A tenant token whose scope
names no document capability is refused on the document routes, and
tokens.issuevalidates a tenant token's scope entries.
Patch Changes
-
#910 by @bobsingor – A layer's versions are read through the layer. The version routes moved from
/v1/docs/:docId/versions…to/v1/docs/:docId/layers/:layerName/versions…, checked like every other layer route.- The list gives the layer's versions (the upload, then the ones its signatures made) and its
baseSha. - A version's signatures, analysis, download and revisions answer only for the upload or the layer's own versions: another layer's version is 404, and a token for one layer can't use another layer's path.
- Tenant tokens read any layer's versions, through that layer.
- The list gives the layer's versions (the upload, then the ones its signatures made) and its
-
#909 by @bobsingor – A share link can say who its visitors are, for a link made for one person:
identityonshares.createandshares.update, the same shape astokens.issue's (migration 043). Every session the link gives carries it, whoever opens the link: a field it sets is never asked, and a visitor's answer for it is ignored. ItsuserIdreplaces the visitor's ownvisitor_<id>; each visit still gets its own layer on a per-visitor link. A link that asks a field its identity sets is refused (400), on create and on an update checked against the link as it will be. -
#907 by @bobsingor – A share link's visitor is remembered on every link except a per-visitor link that doesn't keep visitors' layers: the exchange answers
visitor.keep: truefor shared links too. A visitor who comes back in the same browser keeps their answers to the link's questions and stays the author of what they added, instead of being asked again on every page load and listed as a new visitor each time. -
#910 by @bobsingor – A signature moves only the layer it was made in. Completing a signature writes the new version as that layer's own: only its
base_shamoves, and the document (documents.base_sha, the upload) and every other layer stay as they were.Before, a signature moved the whole document, so on a per-visitor link the next visitor opened the signer's filled-in, signed copy, and layers still on the old version could no longer sign (
StaleBase).- Every layer can sign, over its own base. A layer that signs again signs over its previous version.
- Versions belong to their layer: a layer's versions are numbered along its own chain and stored under the layer, and they're deleted with it.
/layers/:layerName/headreports the layer's own base.- Migration 044 gives each existing signed version its layer, puts a document a signature moved back on its upload, and starts layers that stored nothing on the upload too.
- No document-wide signal any more: the base-changed bus channel is gone. Other replicas reload the signing layer because its version moved.
@embedpdf/core@3.0.0-next.18
Minor Changes
- #907 by @bobsingor – A ready document whose share link asks for a passphrase or answers it got after the document opened shows them as
shareRequest, as a document waiting to open does, anddocuments.retry(id, { sharePassword })orretry(id, { visitor })lets it go on as the same visitor instead of opening it again.
Patch Changes
-
#909 by @bobsingor –
documents.retry(id, { identity })takes the visitor's answers to what a share link asks (was{ visitor }). They join who the page said the user is, the document'sidentityor else the viewer's, and the document opens again with that identity. -
#910 by @bobsingor – The
StaleBaseengine error is gone, and with it itsconflictmapping: a signature moves only its own layer, so every layer can sign.
@embedpdf/engine-core@3.0.0-next.18
Minor Changes
-
#911 by @bobsingor – New
runPageFormFieldsConformancesuite checks thatpages.insertandpages.extractbring form fields, keep same-named fields apart, and survive a save in either mode. -
#907 by @bobsingor –
DocumentSharegetsopenRequest,onOpenRequest()andresume(): a passphrase, or questions asked on open, that a share link gets while a document is open are asked of the visitor, andresume({ sharePassword })orresume({ visitor })lets the document go on as the same visitor.ShareOpenRequestandshareOpenRequestOf()move here from@embedpdf/core, which re-exports the type.
Patch Changes
-
#910 by @bobsingor – The version wire paths and resources are the layer's:
wirePaths.layerVersions,layerVersionSignatures,layerVersionSignatureContents,layerVersionSignatureDigest,layerVersionAnalysis,layerVersionDownloadandlayerVersionRevisiontake the layer name. The resources are nowlayer-versionsandlayer-version-*, so a layer's CDN grant covers only its own versions.The catalog schema is
LayerVersionsSchema({ baseSha, versions }, wasDocumentVersionsSchemawithhead). -
#909 by @bobsingor –
identityis the one word for who the user is, on both engines.OpenOptions.identityis who your page knows the user is: the local engine's session identity, as before; on the cloud engine a document token decides, and a share link takes it as the visitor's answers to what the link asks. The share input'svisitoris gone (passidentityin the options instead), andDocumentShare.resume()takes{ sharePassword, identity }.doc.security.identitysays who a session acts for.The runtime entry exports
devWarn,isDevandresetDevWarningsfor engines: development-only warnings, once per key. -
#910 by @bobsingor –
EngineErrorCode.StaleBaseis gone. A signature no longer moves the document, so no layer is ever behind it and every layer can sign over its own base.
@embedpdf/engine-runtime@3.0.0-next.18
Minor Changes
-
#911 by @bobsingor – New
EPDFDoc_ImportPagescopies pages with the form fields of their widgets, whereFPDF_ImportPagesByIndexleft each copied widget's/Parentnaming an object of the source and wrote no/AcroForm. Each field above the copied widgets is copied once, its/Kidslimited to what was copied, and the copied top-level fields join the destination's/AcroForm /Fields(made when it has none) with the source's/DRresources,/DAand/Q,/NeedAppearances, calculation order andSignaturesExist. A/DRfont is shared only when the destination defines one of that name the same way, key for key (widths and descriptor included, no font program); otherwise it is copied under a free name, and the copied/DAstrings follow, comments and all. A copied pop-up's/Parentnames the copy of its annotation.New
EPDFDoc_GetImportFieldRootslists the top-level fields an import of some pages brings, andEPDFForm_GetFieldPartialNamereads a field's own/Tby object number. -
#913 by @bobsingor – New
EPDFCatalog_GetMetadatareads the document's XMP packet (the catalog's/Metadatastream) decoded, andEPDFCatalog_SetMetadatareplaces it, unfiltered, keeping the stream's object number, or removes it.Saving an encrypted document encrypts its metadata stream, unless the file says
/EncryptMetadata false; it was written in the clear, so readers decrypted the XMP into noise. A metadata stream with a filter is written decoded, where its encoded bytes were written without the filter that decodes them.
@embedpdf/engine-services@3.0.0-next.18
Minor Changes
-
#911 by @bobsingor –
pages.insertandpages.extractbring the form fields of the copied pages' widgets, written into the document's/AcroForm, so the fields stay fillable after a save and in other PDF apps. An extract brings only the widgets on the extracted pages. An inserted top-level field whose name the document already uses comes in asname_2(or the next free number), with its fields under it, so two copies of a filled-in form keep their own values. -
#913 by @bobsingor –
metadata.updatekeeps a document's XMP packet in step with its/Infofields: a field it sets is written to its XMP property (dc:title,dc:creator,dc:description,pdf:Keywords,xmp:CreatorTool,pdf:Producer,xmp:CreateDate,xmp:ModifyDate,pdf:Trapped), added when the packet doesn't have it, and a field it sets tonullis removed from the packet too. A packet'sdc:subjectkeyword list follows the keywords.metadata.custom.updatechanges or removes thepdfx:property Adobe apps copy a custom key to, and never adds one. Everything else in the packet stays as written; a document without XMP gets none, and XMP that isn't well-formed is left as it is.
@cloudpdf/engine@3.0.0-next.18
Patch Changes
-
#910 by @bobsingor – A document's signed bytes (
signatures.getContents,getDigest,downloadRevisionand historyanalyze) are read through its layer's version paths. -
#909 by @bobsingor – A share link takes the page's
identity(theopen()option, or the viewer'sidentity) as the visitor's answers, the same as the visitor typing them: only the fields the link asks, neveruserIdorgroupId. The share input'svisitoris gone.Nothing the page passes is ignored silently: in development the engine warns once when it doesn't use
scope(the document's token says what the session may do), or when the session doesn't act for theidentitypassed, and says why. -
#907 by @bobsingor – A renewal the share link refuses for a passphrase, or for questions asked on open, no longer retries on every request. The document asks once (
doc.share.openRequest,onOpenRequest), reads on with its session until it runs out, then waits forresume(); with nothing listening, a request fails with the refusal. A shared link's visitor is now kept in the visitor store for the next visit, as the server says (visitor.keep).
@cloudpdf/viewer@3.0.0-next.18
Patch Changes
- #909 by @bobsingor – The viewer takes
identityagain: who your page knows the user is. A document token decides for itself, and a share link takes it as the visitor's answers to what the link asks, so the questions it covers aren't asked.src.visitoris gone;scopestays out, since the token says what the user may do.
@embedpdf/core-js-sandbox@3.0.0-next.18
Patch Changes
-
#905 by @bobsingor – PDF JavaScript runs in Next.js apps again.
- Turbopack, the default bundler in Next 16, failed with "Can't resolve '#quickjs-variant'", because it doesn't follow an
importstarget that names another package.#quickjs-variantnow maps to two files of this package,quickjs-variant/browser.jsandquickjs-variant/wasmfile.js, which re-export QuickJS's browser and wasm-file builds. - Production builds on Next before 16.4.1, webpack or Turbopack, broke QuickJS's browser build, so the sandbox never started. QuickJS 0.32.0 embeds its wasm as raw bytes in a template literal, and Next's minifier turned some of them into octal escapes ("Octal escape sequences are not allowed in template strings"). QuickJS is pinned to 0.31.0, which embeds the wasm as base64.
Browsers still get the browser build, with no
.wasmrequest, and Node the wasm-file one. - Turbopack, the default bundler in Next 16, failed with "Can't resolve '#quickjs-variant'", because it doesn't follow an
@embedpdf/viewer-chrome@3.0.0-next.18
Patch Changes
-
#908 by @bobsingor – Hide the Measure tab when the session can neither draw measurements nor set a page's scale: the measurement panel, and the Measure bar's scale button that opens it, now follow the same permission checks as the measurement tools. The scale button is drawn like every other bar button, so the Measure bar is as tall as the other mode bars, and it shows pressed while the panel is open; clicking it again closes the panel.
-
#907 by @bobsingor – Where a document allows searching but not copying text, search opens as a find bar over the top corner of the pages instead of a panel of empty result rows. It shows which match you're on and how many there are,
EnterandShift+Enterstep through them, and its options sit behind a button. In the search panel,EnterandShift+Enterstep through the matches too, and a match the engine finds no text around reads "Result 3" instead of an empty row. -
#907 by @bobsingor – When a share link gets a passphrase or questions while its document is open, the viewer shows the same passphrase or questions form over the document, and the document goes on once they're given.