@embedpdf/core 2.15.0 → 3.0.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +192 -21
- package/dist/index.cjs +864 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +313 -0
- package/dist/index.d.ts +313 -1
- package/dist/index.js +816 -2124
- package/dist/index.js.map +1 -1
- package/package.json +22 -52
- package/README.md +0 -40
- package/dist/lib/base/base-plugin.d.ts +0 -190
- package/dist/lib/index.d.ts +0 -17
- package/dist/lib/plugin/builder.d.ts +0 -11
- package/dist/lib/registry/plugin-registry.d.ts +0 -104
- package/dist/lib/store/actions.d.ts +0 -154
- package/dist/lib/store/index.d.ts +0 -6
- package/dist/lib/store/initial-state.d.ts +0 -31
- package/dist/lib/store/plugin-store.d.ts +0 -48
- package/dist/lib/store/reducer-helpers.d.ts +0 -11
- package/dist/lib/store/reducer.d.ts +0 -4
- package/dist/lib/store/selectors.d.ts +0 -41
- package/dist/lib/store/store.d.ts +0 -106
- package/dist/lib/store/types.d.ts +0 -28
- package/dist/lib/types/errors.d.ts +0 -21
- package/dist/lib/types/permissions.d.ts +0 -55
- package/dist/lib/types/plugin.d.ts +0 -75
- package/dist/lib/utils/dependency-resolver.d.ts +0 -6
- package/dist/lib/utils/event-control.d.ts +0 -58
- package/dist/lib/utils/eventing.d.ts +0 -19
- package/dist/lib/utils/math.d.ts +0 -22
- package/dist/lib/utils/plugin-helpers.d.ts +0 -6
- package/dist/lib/utils/scoped-eventing.d.ts +0 -71
- package/dist/lib/utils/typed-object.d.ts +0 -12
- package/dist/preact/adapter.d.ts +0 -11
- package/dist/preact/index.cjs +0 -2
- package/dist/preact/index.cjs.map +0 -1
- package/dist/preact/index.d.ts +0 -1
- package/dist/preact/index.js +0 -323
- package/dist/preact/index.js.map +0 -1
- package/dist/react/adapter.d.ts +0 -2
- package/dist/react/index.cjs +0 -2
- package/dist/react/index.cjs.map +0 -1
- package/dist/react/index.d.ts +0 -1
- package/dist/react/index.js +0 -322
- package/dist/react/index.js.map +0 -1
- package/dist/shared/components/auto-mount.d.ts +0 -8
- package/dist/shared/components/embed-pdf.d.ts +0 -37
- package/dist/shared/components/index.d.ts +0 -1
- package/dist/shared/context.d.ts +0 -12
- package/dist/shared/hooks/index.d.ts +0 -7
- package/dist/shared/hooks/use-capability.d.ts +0 -16
- package/dist/shared/hooks/use-core-state.d.ts +0 -8
- package/dist/shared/hooks/use-document-permissions.d.ts +0 -35
- package/dist/shared/hooks/use-document-state.d.ts +0 -8
- package/dist/shared/hooks/use-plugin.d.ts +0 -16
- package/dist/shared/hooks/use-registry.d.ts +0 -6
- package/dist/shared/hooks/use-store-state.d.ts +0 -6
- package/dist/shared/index.d.ts +0 -3
- package/dist/shared-preact/components/auto-mount.d.ts +0 -8
- package/dist/shared-preact/components/embed-pdf.d.ts +0 -37
- package/dist/shared-preact/components/index.d.ts +0 -1
- package/dist/shared-preact/context.d.ts +0 -12
- package/dist/shared-preact/hooks/index.d.ts +0 -7
- package/dist/shared-preact/hooks/use-capability.d.ts +0 -16
- package/dist/shared-preact/hooks/use-core-state.d.ts +0 -8
- package/dist/shared-preact/hooks/use-document-permissions.d.ts +0 -35
- package/dist/shared-preact/hooks/use-document-state.d.ts +0 -8
- package/dist/shared-preact/hooks/use-plugin.d.ts +0 -16
- package/dist/shared-preact/hooks/use-registry.d.ts +0 -6
- package/dist/shared-preact/hooks/use-store-state.d.ts +0 -6
- package/dist/shared-preact/index.d.ts +0 -3
- package/dist/shared-react/components/auto-mount.d.ts +0 -8
- package/dist/shared-react/components/embed-pdf.d.ts +0 -37
- package/dist/shared-react/components/index.d.ts +0 -1
- package/dist/shared-react/context.d.ts +0 -12
- package/dist/shared-react/hooks/index.d.ts +0 -7
- package/dist/shared-react/hooks/use-capability.d.ts +0 -16
- package/dist/shared-react/hooks/use-core-state.d.ts +0 -8
- package/dist/shared-react/hooks/use-document-permissions.d.ts +0 -35
- package/dist/shared-react/hooks/use-document-state.d.ts +0 -8
- package/dist/shared-react/hooks/use-plugin.d.ts +0 -16
- package/dist/shared-react/hooks/use-registry.d.ts +0 -6
- package/dist/shared-react/hooks/use-store-state.d.ts +0 -6
- package/dist/shared-react/index.d.ts +0 -3
- package/dist/svelte/components/AutoMount.svelte.d.ts +0 -9
- package/dist/svelte/components/EmbedPDF.svelte.d.ts +0 -40
- package/dist/svelte/components/NestedWrapper.svelte.d.ts +0 -10
- package/dist/svelte/components/index.d.ts +0 -1
- package/dist/svelte/hooks/index.d.ts +0 -6
- package/dist/svelte/hooks/use-capability.svelte.d.ts +0 -14
- package/dist/svelte/hooks/use-core-state.svelte.d.ts +0 -9
- package/dist/svelte/hooks/use-document-permissions.svelte.d.ts +0 -35
- package/dist/svelte/hooks/use-document-state.svelte.d.ts +0 -9
- package/dist/svelte/hooks/use-plugin.svelte.d.ts +0 -14
- package/dist/svelte/hooks/use-registry.svelte.d.ts +0 -17
- package/dist/svelte/index.cjs +0 -2
- package/dist/svelte/index.cjs.map +0 -1
- package/dist/svelte/index.d.ts +0 -2
- package/dist/svelte/index.js +0 -430
- package/dist/svelte/index.js.map +0 -1
- package/dist/vue/components/auto-mount.vue.d.ts +0 -19
- package/dist/vue/components/embed-pdf.vue.d.ts +0 -104
- package/dist/vue/components/index.d.ts +0 -1
- package/dist/vue/components/nested-wrapper.vue.d.ts +0 -19
- package/dist/vue/composables/index.d.ts +0 -7
- package/dist/vue/composables/use-capability.d.ts +0 -15
- package/dist/vue/composables/use-core-state.d.ts +0 -9
- package/dist/vue/composables/use-document-permissions.d.ts +0 -36
- package/dist/vue/composables/use-document-state.d.ts +0 -8
- package/dist/vue/composables/use-plugin.d.ts +0 -8
- package/dist/vue/composables/use-registry.d.ts +0 -1
- package/dist/vue/composables/use-store-state.d.ts +0 -10
- package/dist/vue/context.d.ts +0 -13
- package/dist/vue/index.cjs +0 -2
- package/dist/vue/index.cjs.map +0 -1
- package/dist/vue/index.d.ts +0 -2
- package/dist/vue/index.js +0 -384
- package/dist/vue/index.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,864 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("@embedpdf/models");class e{constructor(){this.dependencyGraph=new Map}addNode(t,e=[]){this.dependencyGraph.set(t,new Set(e))}hasCircularDependencies(){const t=new Set,e=new Set,i=s=>{t.add(s),e.add(s);const o=this.dependencyGraph.get(s)||new Set;for(const n of o)if(t.has(n)){if(e.has(n))return!0}else if(i(n))return!0;return e.delete(s),!1};for(const s of this.dependencyGraph.keys())if(!t.has(s)&&i(s))return!0;return!1}resolveLoadOrder(){if(this.hasCircularDependencies())throw new Error("Circular dependencies detected");const t=[],e=new Set,i=new Set,s=o=>{if(i.has(o))throw new Error("Circular dependency");if(e.has(o))return;i.add(o);const n=this.dependencyGraph.get(o)||new Set;for(const t of n)s(t);i.delete(o),e.add(o),t.push(o)};for(const o of this.dependencyGraph.keys())e.has(o)||s(o);return t}}class i extends Error{constructor(t){super(t),this.name="PluginRegistrationError"}}class s extends Error{constructor(t){super(t),this.name="PluginNotFoundError"}}class o extends Error{constructor(t){super(t),this.name="CircularDependencyError"}}class n extends Error{constructor(t){super(t),this.name="CapabilityNotFoundError"}}class r extends Error{constructor(t){super(t),this.name="CapabilityConflictError"}}class a extends Error{constructor(t){super(t),this.name="PluginInitializationError"}}class c extends Error{constructor(t){super(t),this.name="PluginConfigurationError"}}class u{constructor(t,e){this.store=t,this.pluginId=e}getState(){return this.store.getState().plugins[this.pluginId]}dispatch(t){return this.store.dispatchToPlugin(this.pluginId,t)}subscribeToState(t){return this.store.subscribeToPlugin(this.pluginId,(e,i,s)=>{t(e,i,s)})}onAction(t,e){return this.store.onAction(t,(t,i,s)=>{e(t,i.plugins[this.pluginId],s.plugins[this.pluginId])})}}const d="START_LOADING_DOCUMENT",l="UPDATE_DOCUMENT_LOADING_PROGRESS",h="SET_DOCUMENT_LOADED",g="SET_DOCUMENT_ERROR",p="RETRY_LOADING_DOCUMENT",m="CLOSE_DOCUMENT",f="SET_ACTIVE_DOCUMENT",y="REORDER_DOCUMENTS",S="MOVE_DOCUMENT",w="UPDATE_DOCUMENT_SECURITY",b="REFRESH_DOCUMENT",P="REFRESH_PAGES",E="SET_PAGES",D="SET_SCALE",v="SET_ROTATION",C="SET_DEFAULT_SCALE",I="SET_DEFAULT_ROTATION",R=[d,l,h,m,f,g,p,b,P,E,D,v,C,I,y,S,w];class A{constructor(t,e){this.initialCoreState=e,this.pluginReducers={},this.listeners=[],this.pluginListeners={},this.isDispatching=!1,this.state={core:e,plugins:{}},this.coreReducer=t}addPluginReducer(t,e,i){this.state.plugins[t]=i,this.pluginReducers[t]=e}dispatchToCore(t){if(!this.coreReducer)return this.getState();if(this.isDispatching)throw new Error("Reducers may not dispatch actions. To trigger cascading actions, dispatch from a listener callback instead.");const e=this.getState();try{this.isDispatching=!0,this.state.core=this.coreReducer(this.state.core,t)}finally{this.isDispatching=!1}return this.listeners.forEach(i=>{const s=this.getState();i(t,s,e)}),this.getState()}dispatchToPlugin(t,e,i=!0){if(this.isDispatching)throw new Error("Reducers may not dispatch actions. To trigger cascading actions, dispatch from a listener callback instead.");const s=this.getState(),o=this.pluginReducers[t];if(!o)return s.plugins[t];const n=s.plugins[t];try{this.isDispatching=!0;const i=o(n,e);this.state.plugins[t]=i}finally{this.isDispatching=!1}return i&&this.listeners.forEach(t=>{const i=this.getState();t(e,i,s)}),this.pluginListeners[t]&&this.pluginListeners[t].forEach(i=>{const s=this.getState().plugins[t];i(e,s,n)}),this.getState().plugins[t]}dispatch(t){if(this.isDispatching)throw new Error("Reducers may not dispatch actions. To trigger cascading actions, dispatch from a listener callback instead.");const e=this.getState();try{this.isDispatching=!0,this.isCoreAction(t)&&(this.state.core=this.coreReducer(this.state.core,t));for(const i in this.pluginReducers){const s=this.pluginReducers[i],o=e.plugins[i];s&&(this.state.plugins[i]=s(o,t))}}finally{this.isDispatching=!1}return this.listeners.forEach(i=>{const s=this.getState();i(t,s,e)}),this.getState()}getState(){if(this.isDispatching)throw new Error("You may not call store.getState() while the reducer is executing. The reducer has already received the state as an argument. Pass it down from the top reducer instead of reading it from the store.");return{core:{...this.state.core},plugins:{...this.state.plugins}}}subscribe(t){if(this.isDispatching)throw new Error("You may not call store.subscribe() while the reducer is executing. If you would like to be notified after the store has been updated, subscribe from a component and invoke store.getState() in the callback to access the latest state.");return this.listeners.push(t),()=>{if(this.isDispatching)throw new Error("You may not unsubscribe from a store listener while the reducer is executing.");this.listeners=this.listeners.filter(e=>e!==t)}}subscribeToPlugin(t,e){if(!(t in this.state.plugins))throw new Error(`Plugin state not found for plugin "${t}". Did you forget to call addPluginReducer?`);if(this.isDispatching)throw new Error("You may not call store.subscribeToPlugin() while the reducer is executing.");return this.pluginListeners[t]||(this.pluginListeners[t]=[]),this.pluginListeners[t].push(e),()=>{if(this.isDispatching)throw new Error("You may not unsubscribe from a store listener while the reducer is executing.");this.pluginListeners[t]=this.pluginListeners[t].filter(t=>t!==e),0===this.pluginListeners[t].length&&delete this.pluginListeners[t]}}onAction(t,e){return this.subscribe((i,s,o)=>{i.type===t&&e(i,s,o)})}getPluginStore(t){if(!(t in this.state.plugins))throw new Error(`Plugin state not found for plugin "${t}". Did you forget to call addPluginReducer?`);return new u(this,t)}isCoreAction(t){return R.includes(t.type)}destroy(){var t,e;this.listeners.length=0;for(const i in this.pluginListeners)null==(e=null==(t=this.pluginListeners[i])?void 0:t.splice)||e.call(t,0);this.pluginListeners={},this.pluginReducers={},this.state.plugins={},this.state.core={...this.initialCoreState}}}const T=e=>({documents:{},documentOrder:[],activeDocumentId:null,defaultScale:(null==e?void 0:e.defaultScale)??1,defaultRotation:(null==e?void 0:e.defaultRotation)??t.Rotation.Degree0,globalPermissions:null==e?void 0:e.permissions}),x={print:t.PdfPermissionFlag.Print,modifyContents:t.PdfPermissionFlag.ModifyContents,copyContents:t.PdfPermissionFlag.CopyContents,modifyAnnotations:t.PdfPermissionFlag.ModifyAnnotations,fillForms:t.PdfPermissionFlag.FillForms,extractForAccessibility:t.PdfPermissionFlag.ExtractForAccessibility,assembleDocument:t.PdfPermissionFlag.AssembleDocument,printHighQuality:t.PdfPermissionFlag.PrintHighQuality},F=[t.PdfPermissionFlag.Print,t.PdfPermissionFlag.ModifyContents,t.PdfPermissionFlag.CopyContents,t.PdfPermissionFlag.ModifyAnnotations,t.PdfPermissionFlag.FillForms,t.PdfPermissionFlag.ExtractForAccessibility,t.PdfPermissionFlag.AssembleDocument,t.PdfPermissionFlag.PrintHighQuality],O={[t.PdfPermissionFlag.Print]:"print",[t.PdfPermissionFlag.ModifyContents]:"modifyContents",[t.PdfPermissionFlag.CopyContents]:"copyContents",[t.PdfPermissionFlag.ModifyAnnotations]:"modifyAnnotations",[t.PdfPermissionFlag.FillForms]:"fillForms",[t.PdfPermissionFlag.ExtractForAccessibility]:"extractForAccessibility",[t.PdfPermissionFlag.AssembleDocument]:"assembleDocument",[t.PdfPermissionFlag.PrintHighQuality]:"printHighQuality"};function M(t,e){if(!t)return;if(e in t)return t[e];const i=O[e];return i&&i in t?t[i]:void 0}function _(e,i,s){var o;const n=e.documents[i],r=null==n?void 0:n.permissions,a=e.globalPermissions,c=(null==(o=null==n?void 0:n.document)?void 0:o.permissions)??t.PdfPermissionFlag.AllowAll,u=M(null==r?void 0:r.overrides,s);if(void 0!==u)return u;const d=M(null==a?void 0:a.overrides,s);if(void 0!==d)return d;return!((null==r?void 0:r.enforceDocumentPermissions)??(null==a?void 0:a.enforceDocumentPermissions)??!0)||0!==(c&s)}function L(t,e){return F.reduce((i,s)=>_(t,e,s)?i|s:i,0)}function N(t,e,i){const s=t.activeDocumentId;if(s!==e)return s;if(void 0!==i)return i&&t.documents[i]?i:null;const o=t.documentOrder.indexOf(e);return-1===o?null:o>0?t.documentOrder[o-1]:o<t.documentOrder.length-1?t.documentOrder[o+1]:null}const U=(t,e)=>{switch(e.type){case d:{const{documentId:i,name:s,scale:o,rotation:n,passwordProvided:r,autoActivate:a=!0,permissions:c}=e.payload,u={id:i,name:s,status:"loading",loadingProgress:0,error:null,document:null,scale:o??t.defaultScale,rotation:n??t.defaultRotation,passwordProvided:r??!1,pageRefreshVersions:{},permissions:c,loadStartedAt:Date.now()};return{...t,documents:{...t.documents,[i]:u},documentOrder:[...t.documentOrder,i],activeDocumentId:a||!t.activeDocumentId?i:t.activeDocumentId}}case l:{const{documentId:i,progress:s}=e.payload,o=t.documents[i];return o&&"loading"===o.status?{...t,documents:{...t.documents,[i]:{...o,loadingProgress:s}}}:t}case h:{const{documentId:i,document:s}=e.payload,o=t.documents[i];return o?{...t,documents:{...t.documents,[i]:{...o,status:"loaded",document:s,error:null,errorCode:void 0,errorDetails:void 0,passwordProvided:void 0,loadedAt:Date.now()}}}:t}case g:{const{documentId:i,error:s,errorCode:o,errorDetails:n}=e.payload,r=t.documents[i];return r?{...t,documents:{...t.documents,[i]:{...r,status:"error",error:s,errorCode:o,errorDetails:n}}}:t}case p:{const{documentId:i,passwordProvided:s}=e.payload,o=t.documents[i];return o&&"error"===o.status?{...t,documents:{...t.documents,[i]:{...o,status:"loading",loadingProgress:0,error:null,errorCode:void 0,errorDetails:void 0,passwordProvided:s??!1,loadStartedAt:Date.now()}}}:t}case m:{const{documentId:i,nextActiveDocumentId:s}=e.payload,{[i]:o,...n}=t.documents;return{...t,documents:n,documentOrder:t.documentOrder.filter(t=>t!==i),activeDocumentId:N(t,i,s)}}case S:{const{documentId:i,toIndex:s}=e.payload,o=function(t,e,i){const s=t.indexOf(e);if(-1===s)return null;if(i<0||i>=t.length)return null;if(s===i)return null;const o=[...t];return o.splice(s,1),o.splice(i,0,e),o}(t.documentOrder,i,s);return o?{...t,documentOrder:o}:t}case y:return{...t,documentOrder:e.payload};case f:return{...t,activeDocumentId:e.payload};case D:{const{scale:i,documentId:s}=e.payload,o=s??t.activeDocumentId;if(!o)return t;const n=t.documents[o];return n?{...t,documents:{...t.documents,[o]:{...n,scale:i}}}:t}case v:{const{rotation:i,documentId:s}=e.payload,o=s??t.activeDocumentId;if(!o)return t;const n=t.documents[o];return n?{...t,documents:{...t.documents,[o]:{...n,rotation:i}}}:t}case P:{const{documentId:i,pageIndexes:s}=e.payload,o=t.documents[i];if(!o)return t;const n={...o.pageRefreshVersions};for(const t of s)n[t]=(n[t]||0)+1;return{...t,documents:{...t.documents,[i]:{...o,pageRefreshVersions:n}}}}case w:{const{documentId:i,permissions:s,isOwnerUnlocked:o}=e.payload,n=t.documents[i];return(null==n?void 0:n.document)?{...t,documents:{...t.documents,[i]:{...n,document:{...n.document,permissions:s,isOwnerUnlocked:o}}}}:t}default:return t}};class k{constructor(t,e){this.handler=t,this.options=e,this.lastRun=0,this.handle=t=>{"debounce"===this.options.mode?this.debounce(t):this.throttle(t)}}debounce(t){this.timeoutId&&window.clearTimeout(this.timeoutId),this.timeoutId=window.setTimeout(()=>{this.handler(t),this.timeoutId=void 0},this.options.wait)}throttle(t){if("debounce"===this.options.mode)return;const e=Date.now(),i=this.options.throttleMode||"leading-trailing";e-this.lastRun>=this.options.wait&&("leading-trailing"===i&&this.handler(t),this.lastRun=e),this.timeoutId&&window.clearTimeout(this.timeoutId),this.timeoutId=window.setTimeout(()=>{this.handler(t),this.lastRun=Date.now(),this.timeoutId=void 0},this.options.wait-(e-this.lastRun))}destroy(){this.timeoutId&&window.clearTimeout(this.timeoutId)}}class z{constructor(t,e){this.handler=t,this.options=e,this.controls=new Map,this.handle=t=>{const e=String(this.options.keyExtractor(t));let i=this.controls.get(e);i||(i=new k(this.handler,this.baseOptions),this.controls.set(e,i)),i.handle(t)},this.baseOptions={mode:e.mode,wait:e.wait,..."throttle"===e.mode&&"throttleMode"in e?{throttleMode:e.throttleMode}:{}}}destroy(){for(const t of this.controls.values())t.destroy();this.controls.clear()}}function $(t){return"keyExtractor"in t}function G(t,e,i){if(t===e)return!0;if(null==t||null==e)return t===e;const s=typeof t;if(s!==typeof e)return!1;if("object"===s){i||(i=new Set);const s=function(t,e){return`${H(t)}__${H(e)}`}(t,e);if(i.has(s))return!0;i.add(s);const o=Array.isArray(t),n=Array.isArray(e);return o&&n?function(t,e,i){if(t.length!==e.length)return!1;const s=new Array(e.length).fill(!1);t:for(let o=0;o<t.length;o++){const n=t[o];for(let t=0;t<e.length;t++)if(!s[t]&&G(n,e[t],i)){s[t]=!0;continue t}return!1}return!0}(t,e,i):!o&&!n&&function(t,e,i){const s=Object.keys(t).sort(),o=Object.keys(e).sort();if(s.length!==o.length)return!1;for(let n=0;n<s.length;n++)if(s[n]!==o[n])return!1;for(const n of s){if(!G(t[n],e[n],i))return!1}return!0}(t,e,i)}return!1}let j=0;const q=new WeakMap;function H(t){return q.has(t)||q.set(t,++j),q.get(t)}class Y{constructor(t){this.autoMountElements=[],this.package=t}addUtility(t){return this.autoMountElements.push({component:t,type:"utility"}),this}addWrapper(t){return this.autoMountElements.push({component:t,type:"wrapper"}),this}build(){return{...this.package,autoMountElements:()=>this.autoMountElements}}}exports.ALL_PERMISSION_FLAGS=F,exports.ALL_PERMISSION_NAMES=["print","modifyContents","copyContents","modifyAnnotations","fillForms","extractForAccessibility","assembleDocument","printHighQuality"],exports.BasePlugin=class{constructor(t,e){if(this.id=t,this.registry=e,this.cooldownActions={},this.debouncedTimeouts={},this.unsubscribeFromState=null,this.unsubscribeFromCoreStore=null,this.unsubscribeFromStartLoadingDocument=null,this.unsubscribeFromSetDocumentLoaded=null,this.unsubscribeFromCloseDocument=null,this.unsubscribeFromSetScale=null,this.unsubscribeFromSetRotation=null,t!==this.constructor.id)throw new Error(`Plugin ID mismatch: ${t} !== ${this.constructor.id}`);this.engine=this.registry.getEngine(),this.logger=this.registry.getLogger(),this.coreStore=this.registry.getStore(),this.pluginStore=this.coreStore.getPluginStore(this.id),this.unsubscribeFromState=this.pluginStore.subscribeToState((t,e,i)=>{this.onStoreUpdated(i,e)}),this.unsubscribeFromCoreStore=this.coreStore.subscribe((t,e,i)=>{this.onCoreStoreUpdated(i,e),e.core.activeDocumentId!==i.core.activeDocumentId&&this.onActiveDocumentChanged(i.core.activeDocumentId,e.core.activeDocumentId)}),this.unsubscribeFromStartLoadingDocument=this.coreStore.onAction(d,t=>{this.onDocumentLoadingStarted(t.payload.documentId)}),this.unsubscribeFromSetDocumentLoaded=this.coreStore.onAction(h,t=>{this.onDocumentLoaded(t.payload.documentId)}),this.unsubscribeFromCloseDocument=this.coreStore.onAction(m,t=>{this.onDocumentClosed(t.payload.documentId)}),this.unsubscribeFromSetScale=this.coreStore.onAction(D,(t,e)=>{const i=t.payload.documentId??e.core.activeDocumentId;i&&this.onScaleChanged(i,t.payload.scale)}),this.unsubscribeFromSetRotation=this.coreStore.onAction(v,(t,e)=>{const i=t.payload.documentId??e.core.activeDocumentId;i&&this.onRotationChanged(i,t.payload.rotation)}),this.readyPromise=new Promise(t=>{this.readyResolve=t}),this.readyResolve()}provides(){if(!this._capability){const t=this.buildCapability();this._capability=Object.freeze(t)}return this._capability}get state(){return this.pluginStore.getState()}get coreState(){return this.coreStore.getState()}getState(){return this.pluginStore.getState()}getCoreState(){return this.coreStore.getState()}dispatchCoreAction(t){return this.coreStore.dispatchToCore(t)}dispatchToAllPlugins(t){return this.coreStore.dispatch(t)}dispatch(t){return this.pluginStore.dispatch(t)}cooldownDispatch(t,e=100){const i=Date.now();return i-(this.cooldownActions[t.type]||0)>=e&&(this.cooldownActions[t.type]=i,this.dispatch(t),!0)}debouncedDispatch(t,e=100){const i=t.type;this.debouncedTimeouts[i]&&clearTimeout(this.debouncedTimeouts[i]),this.debouncedTimeouts[i]=setTimeout(()=>{this.dispatch(t),delete this.debouncedTimeouts[i]},e)}cancelDebouncedDispatch(t){this.debouncedTimeouts[t]&&(clearTimeout(this.debouncedTimeouts[t]),delete this.debouncedTimeouts[t])}subscribe(t){return this.pluginStore.subscribeToState(t)}subscribeToCoreStore(t){return this.coreStore.subscribe(t)}onStoreUpdated(t,e){}onCoreStoreUpdated(t,e){}onDocumentLoadingStarted(t){}onDocumentLoaded(t){}onDocumentClosed(t){}onActiveDocumentChanged(t,e){}onScaleChanged(t,e){}onRotationChanged(t,e){}destroy(){Object.values(this.debouncedTimeouts).forEach(t=>{clearTimeout(t)}),this.debouncedTimeouts={},this.unsubscribeFromState&&(this.unsubscribeFromState(),this.unsubscribeFromState=null),this.unsubscribeFromCoreStore&&(this.unsubscribeFromCoreStore(),this.unsubscribeFromCoreStore=null),this.unsubscribeFromStartLoadingDocument&&(this.unsubscribeFromStartLoadingDocument(),this.unsubscribeFromStartLoadingDocument=null),this.unsubscribeFromSetDocumentLoaded&&(this.unsubscribeFromSetDocumentLoaded(),this.unsubscribeFromSetDocumentLoaded=null),this.unsubscribeFromCloseDocument&&(this.unsubscribeFromCloseDocument(),this.unsubscribeFromCloseDocument=null),this.unsubscribeFromSetScale&&(this.unsubscribeFromSetScale(),this.unsubscribeFromSetScale=null),this.unsubscribeFromSetRotation&&(this.unsubscribeFromSetRotation(),this.unsubscribeFromSetRotation=null)}ready(){return this.readyPromise}markReady(){this.readyResolve()}resetReady(){this.readyPromise=new Promise(t=>{this.readyResolve=t})}getActiveDocumentId(){const t=this.coreState.core.activeDocumentId;if(!t)throw new Error("No active document");return t}getActiveDocumentIdOrNull(){return this.coreState.core.activeDocumentId}getCoreDocument(t){const e=t??this.getActiveDocumentIdOrNull();return e?this.coreState.core.documents[e]??null:null}getCoreDocumentOrThrow(t){const e=this.getCoreDocument(t);if(!e)throw new Error(`Document not found: ${t??"active"}`);return e}getDocumentPermissions(e){const i=e??this.coreState.core.activeDocumentId;return i?L(this.coreState.core,i):t.PdfPermissionFlag.AllowAll}checkPermission(t,...e){const i=t??this.coreState.core.activeDocumentId;return!i||e.every(t=>_(this.coreState.core,i,t))}requirePermission(e,...i){const s=e??this.coreState.core.activeDocumentId;if(!s)return;const o=[];for(const t of i)_(this.coreState.core,s,t)||o.push(t);if(o.length>0){const e=L(this.coreState.core,s);throw new t.PermissionDeniedError(o,e)}}},exports.CLOSE_DOCUMENT=m,exports.CORE_ACTION_TYPES=R,exports.CapabilityConflictError=r,exports.CapabilityNotFoundError=n,exports.CircularDependencyError=o,exports.DependencyResolver=e,exports.EventControl=k,exports.KeyedEventControl=z,exports.MOVE_DOCUMENT=S,exports.PERMISSION_FLAG_TO_NAME=O,exports.PERMISSION_NAME_TO_FLAG=x,exports.PluginConfigurationError=c,exports.PluginInitializationError=a,exports.PluginNotFoundError=s,exports.PluginPackageBuilder=Y,exports.PluginRegistrationError=i,exports.PluginRegistry=class{constructor(i,s){this.plugins=new Map,this.manifests=new Map,this.capabilities=new Map,this.status=new Map,this.configurations=new Map,this.initPromise=null,this.pendingRegistrations=[],this.processingRegistrations=[],this.initialized=!1,this.isInitializing=!1,this.pluginsReadyPromise=null,this.destroyed=!1,this.resolver=new e,this.engine=i,this.initialCoreState=T(s),this.store=new A(U,this.initialCoreState),this.logger=(null==s?void 0:s.logger)??new t.NoopLogger}getLogger(){return this.logger}registerPlugin(t,e){if(this.initialized&&!this.isInitializing)throw new i("Cannot register plugins after initialization");this.validateManifest(t.manifest),this.store.addPluginReducer(t.manifest.id,t.reducer,"function"==typeof t.initialState?t.initialState(this.initialCoreState,{...t.manifest.defaultConfig,...e}):t.initialState),this.pendingRegistrations.push({package:t,config:e})}getStore(){return this.store}getEngine(){return this.engine}pluginsReady(){return this.pluginsReadyPromise||(this.pluginsReadyPromise=(async()=>{this.initialized||await this.initialize();const t=Array.from(this.plugins.values()).map(t=>"function"==typeof t.ready?t.ready():Promise.resolve());await Promise.all(t)})()),this.pluginsReadyPromise}async initialize(){if(this.destroyed)throw new i("Registry has been destroyed");return this.initPromise||(this.initPromise=(async()=>{if(this.initialized)throw new i("Registry is already initialized");this.isInitializing=!0;try{if(this.destroyed)return;for(;this.pendingRegistrations.length>0;){if(this.destroyed)return;this.processingRegistrations=[...this.pendingRegistrations],this.pendingRegistrations=[];for(const e of this.processingRegistrations){const t=new Set,i=[...e.package.manifest.requires,...e.package.manifest.optional];for(const e of i){const i=this.processingRegistrations.find(t=>t.package.manifest.provides.includes(e));i&&t.add(i.package.manifest.id)}this.resolver.addNode(e.package.manifest.id,[...t])}const t=this.resolver.resolveLoadOrder();for(const e of t){const t=this.processingRegistrations.find(t=>t.package.manifest.id===e);this.instantiatePlugin(t.package.manifest,t.package.create,t.config)}for(const e of t)await this.runPluginInitialization(e);this.processingRegistrations=[],this.resolver=new e}this.initialized=!0}catch(t){if(t instanceof Error)throw new o(`Failed to resolve plugin dependencies: ${t.message}`);throw t}finally{this.isInitializing=!1}})()),this.initPromise}instantiatePlugin(t,e,s){const o={...t.defaultConfig,...s};this.validateConfig(t.id,o,t.defaultConfig);const n=e(this,o);this.validatePlugin(n);for(const r of t.provides){if(this.capabilities.has(r))throw new i(`Capability ${r} is already provided by plugin ${this.capabilities.get(r)}`);this.capabilities.set(r,t.id)}this.plugins.set(t.id,n),this.manifests.set(t.id,t),this.status.set(t.id,"registered"),this.configurations.set(t.id,o)}async runPluginInitialization(t){const e=this.plugins.get(t);if(!e)return;const s=this.manifests.get(t),o=this.configurations.get(t);for(const r of s.requires)if(!this.capabilities.has(r))throw new i(`Missing required capability: ${r} for plugin ${t}`);this.logger.debug("PluginRegistry","InitializePlugin",`Initializing plugin ${t}`);try{e.initialize&&await e.initialize(o),this.status.set(t,"active"),this.logger.info("PluginRegistry","PluginInitialized",`Plugin ${t} initialized successfully`)}catch(n){throw this.status.set(t,"error"),this.logger.error("PluginRegistry","InitializationFailed",`Plugin ${t} initialization failed`,{error:n}),n}}getPluginConfig(t){const e=this.configurations.get(t);if(!e)throw new s(`Configuration for plugin ${t} not found`);return e}validateConfig(t,e,i){const s=Object.keys(i).filter(t=>!e.hasOwnProperty(t));if(s.length>0)throw new c(`Missing required configuration keys for plugin ${t}: ${s.join(", ")}`)}async updatePluginConfig(t,e){const i=this.getPlugin(t);if(!i)throw new s(`Plugin ${t} not found`);const o=this.manifests.get(t),n=this.configurations.get(t);if(!o||!n)throw new s(`Plugin ${t} not found`);const r={...n,...e};this.validateConfig(t,r,o.defaultConfig),this.configurations.set(t,r),i.initialize&&await i.initialize(r)}registerPluginBatch(t){for(const e of t)this.registerPlugin(e.package,e.config)}async unregisterPlugin(t){const e=this.plugins.get(t);if(!e)throw new s(`Plugin ${t} is not registered`);const o=this.manifests.get(t);if(!o)throw new s(`Manifest for plugin ${t} not found`);for(const[s,r]of this.manifests.entries()){if(s===t)continue;if([...r.requires,...r.optional].some(t=>o.provides.includes(t)))throw new i(`Cannot unregister plugin ${t}: plugin ${s} depends on it`)}try{e.destroy&&await e.destroy();for(const t of o.provides)this.capabilities.delete(t);this.plugins.delete(t),this.manifests.delete(t),this.status.delete(t)}catch(n){if(n instanceof Error)throw new Error(`Failed to unregister plugin ${t}: ${n.message}`);throw n}}getPlugin(t){const e=this.plugins.get(t);return e||null}getCapabilityProvider(t){const e=this.capabilities.get(t);return e?this.getPlugin(e):null}hasCapability(t){return this.capabilities.has(t)}getAllPlugins(){return Array.from(this.plugins.values())}getPluginStatus(t){const e=this.status.get(t);if(!e)throw new s(`Plugin ${t} not found`);return e}validatePlugin(t){if(!t.id)throw new i("Plugin must have an id")}validateManifest(t){if(!t.id)throw new i("Manifest must have an id");if(!t.name)throw new i("Manifest must have a name");if(!t.version)throw new i("Manifest must have a version");if(!Array.isArray(t.provides))throw new i("Manifest must have a provides array");if(!Array.isArray(t.requires))throw new i("Manifest must have a requires array");if(!Array.isArray(t.optional))throw new i("Manifest must have an optional array")}isDestroyed(){return this.destroyed}async destroy(){var t;if(this.destroyed)throw new i("Registry has already been destroyed");this.destroyed=!0;try{await this.initPromise}catch{}for(const e of Array.from(this.plugins.values()).reverse())await(null==(t=e.destroy)?void 0:t.call(e));this.store.destroy(),this.plugins.clear(),this.manifests.clear(),this.capabilities.clear(),this.status.clear(),this.pendingRegistrations.length=0,this.processingRegistrations.length=0}},exports.REFRESH_DOCUMENT=b,exports.REFRESH_PAGES=P,exports.REORDER_DOCUMENTS=y,exports.RETRY_LOADING_DOCUMENT=p,exports.SET_ACTIVE_DOCUMENT=f,exports.SET_DEFAULT_ROTATION=I,exports.SET_DEFAULT_SCALE=C,exports.SET_DOCUMENT_ERROR=g,exports.SET_DOCUMENT_LOADED=h,exports.SET_PAGES=E,exports.SET_ROTATION=v,exports.SET_SCALE=D,exports.START_LOADING_DOCUMENT=d,exports.UPDATE_DOCUMENT_LOADING_PROGRESS=l,exports.UPDATE_DOCUMENT_SECURITY=w,exports.arePropsEqual=G,exports.clamp=function(t,e,i){return t<e?e:t>i?i:t},exports.closeDocument=(t,e)=>({type:m,payload:{documentId:t,nextActiveDocumentId:e}}),exports.createBehaviorEmitter=function(t,e=G){const i=new Set,s=new Map;let o=t;const n=(t,e)=>{let n=t,r=()=>{};if(e){if($(e)){const i=new z(t,e);n=i.handle,r=()=>i.destroy()}else{const i=new k(t,e);n=i.handle,r=()=>i.destroy()}s.set(t,{wrapped:n,destroy:r})}return void 0!==o&&n(o),i.add(n),()=>{i.delete(n),r(),s.delete(t)}};return{get value(){return o},emit(t=void 0){void 0!==o&&e(o,t)||(o=t,(t=>{i.forEach(e=>e(t))})(t))},on:n,off(t){const e=s.get(t);e?(i.delete(e.wrapped),e.destroy(),s.delete(t)):i.delete(t)},clear(){i.clear(),s.forEach(t=>t.destroy()),s.clear()},select:(t,e=G)=>(i,s)=>{let r;if(void 0!==o){const e=t(o);r=e,i(e)}return n(s=>{const o=t(s);void 0!==r&&e(r,o)||(r=o,i(o))},s)}}},exports.createEmitter=function(){const t=new Set;return{emit:(e=void 0)=>t.forEach(t=>t(e)),on:e=>(t.add(e),()=>t.delete(e)),off:e=>t.delete(e),clear:()=>t.clear()}},exports.createPluginPackage=function(t){return new Y(t)},exports.createPluginRegistration=function(t,e){return{package:t,config:e}},exports.createScopedEmitter=function(t,e){const i=(null==e?void 0:e.cache)??!0,s=(null==e?void 0:e.equality)??G,o=new Map,n=new Map,r=new Map,a=new Set,c=new Map,u=t=>String(t);return{emit(e,r){const c=u(e);if(i){const t=o.get(c);if(void 0!==t&&s(t,r))return;o.set(c,r)}const d=n.get(c);d&&d.forEach(t=>t(r));const l=t(e,r);a.forEach(t=>t(l))},forScope(t){const e=u(t);return(t,s)=>{const a=(t=>{let e=n.get(t);return e||(e=new Set,n.set(t,e)),e})(e),c=(t=>{let e=r.get(t);return e||(e=new Map,r.set(t,e)),e})(e);let u=t,d=()=>{};if(s){if($(s)){const e=new z(t,s);u=e.handle,d=()=>e.destroy()}else{const e=new k(t,s);u=e.handle,d=()=>e.destroy()}c.set(t,{wrapped:u,destroy:d})}if(i){const t=o.get(e);void 0!==t&&u(t)}return a.add(u),()=>{a.delete(u),d(),c.delete(t),0===a.size&&n.delete(e),0===c.size&&r.delete(e)}}},onGlobal:(t,e)=>{let i=t,s=()=>{};if(e){if($(e)){const o=new z(t,e);i=o.handle,s=()=>o.destroy()}else{const o=new k(t,e);i=o.handle,s=()=>o.destroy()}c.set(t,{wrapped:i,destroy:s})}return a.add(i),()=>{a.delete(i),s(),c.delete(t)}},getValue:t=>i?o.get(u(t)):void 0,getScopes:()=>i?Array.from(o.keys()):Array.from(n.keys()),clearScope(t){const e=u(t);i&&o.delete(e);const s=n.get(e);s&&(s.clear(),n.delete(e));const a=r.get(e);a&&(a.forEach(t=>t.destroy()),a.clear(),r.delete(e))},clear(){i&&o.clear(),n.forEach(t=>t.clear()),n.clear(),r.forEach(t=>{t.forEach(t=>t.destroy()),t.clear()}),r.clear(),a.clear(),c.forEach(t=>t.destroy()),c.clear()}}},exports.enumEntries=function(t){return Object.entries(t).map(([t,e])=>{const i=Number(t);return[Number.isFinite(i)&&""!==t.trim()?i:t,e]})},exports.getActiveDocumentState=t=>t.activeDocumentId?t.documents[t.activeDocumentId]??null:null,exports.getDocumentCount=t=>Object.keys(t.documents).length,exports.getDocumentIds=t=>Object.keys(t.documents),exports.getDocumentState=(t,e)=>t.documents[e]??null,exports.getEffectivePermission=_,exports.getEffectivePermissions=L,exports.getPermissionOverride=M,exports.hasAutoMountElements=function(t){return"autoMountElements"in t&&"function"==typeof t.autoMountElements},exports.initialCoreState=T,exports.isDocumentLoaded=(t,e)=>!!t.documents[e],exports.isKeyedOptions=$,exports.moveDocument=(t,e)=>({type:S,payload:{documentId:t,toIndex:e}}),exports.refreshDocument=(t,e)=>({type:b,payload:{documentId:t,document:e}}),exports.refreshPages=(t,e)=>({type:P,payload:{documentId:t,pageIndexes:e}}),exports.reorderDocuments=t=>({type:y,payload:t}),exports.retryLoadingDocument=(t,e)=>({type:p,payload:{documentId:t,passwordProvided:e}}),exports.setActiveDocument=t=>({type:f,payload:t}),exports.setDefaultRotation=t=>({type:I,payload:t}),exports.setDefaultScale=t=>({type:C,payload:t}),exports.setDocumentError=(t,e,i,s)=>({type:g,payload:{documentId:t,error:e,errorCode:i,errorDetails:s}}),exports.setDocumentLoaded=(t,e)=>({type:h,payload:{documentId:t,document:e}}),exports.setPages=(t,e)=>({type:E,payload:{documentId:t,pages:e}}),exports.setRotation=(t,e)=>({type:v,payload:{rotation:t,documentId:e}}),exports.setScale=(t,e)=>({type:D,payload:{scale:t,documentId:e}}),exports.startLoadingDocument=(t,e,i,s,o,n,r)=>({type:d,payload:{documentId:t,name:e,scale:i,rotation:s,passwordProvided:o,autoActivate:n,permissions:r}}),exports.updateDocumentLoadingProgress=(t,e)=>({type:l,payload:{documentId:t,progress:e}}),exports.updateDocumentSecurity=(t,e,i)=>({type:w,payload:{documentId:t,permissions:e,isOwnerUnlocked:i}});
|
|
2
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _embedpdf_engine_core_runtime = require("@embedpdf/engine-core/runtime");
|
|
3
|
+
//#region src/types.ts
|
|
4
|
+
/** Kernel-emitted document-lifecycle actions; plugins may react via `onAction`. */
|
|
5
|
+
const CORE_DOCUMENT_ADDED = "@@core/document-added";
|
|
6
|
+
const CORE_DOCUMENT_REMOVED = "@@core/document-removed";
|
|
7
|
+
const CORE_ACTIVE_CHANGED = "@@core/active-changed";
|
|
8
|
+
const CORE_ORDER_CHANGED = "@@core/order-changed";
|
|
9
|
+
/** A document's page registry was replaced by a mutation event (rotate/move/delete). */
|
|
10
|
+
const CORE_DOCUMENT_PAGES_UPDATED = "@@core/document-pages-updated";
|
|
11
|
+
/** A tab slot was reserved: `open()` was called; the document is loading. */
|
|
12
|
+
const CORE_DOCUMENT_OPENING = "@@core/document-opening";
|
|
13
|
+
/** The engine reports the document needs a password (`documents.unlock`). */
|
|
14
|
+
const CORE_DOCUMENT_LOCKED = "@@core/document-locked";
|
|
15
|
+
/** The open failed; the tab stays with `status: 'error'` until closed. */
|
|
16
|
+
const CORE_DOCUMENT_OPEN_FAILED = "@@core/document-open-failed";
|
|
17
|
+
/** Field-wise DocInfo equality — the ONE definition every adapter's reactive
|
|
18
|
+
* `docs` read keys on, so a new lifecycle field can never silently stop
|
|
19
|
+
* re-rendering one framework's tab bar. */
|
|
20
|
+
const docInfoEquals = (a, b) => a.id === b.id && a.name === b.name && a.status === b.status && a.pageCount === b.pageCount && a.passwordProvided === b.passwordProvided;
|
|
21
|
+
const docInfoListEquals = (a, b) => a === b || a.length === b.length && a.every((d, i) => docInfoEquals(d, b[i]));
|
|
22
|
+
/** Built-in token for the document registry capability (provided by the kernel). */
|
|
23
|
+
const DocumentsToken = { name: "documents" };
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/store.ts
|
|
26
|
+
function createStore(report = console.error) {
|
|
27
|
+
let core = {
|
|
28
|
+
documents: {},
|
|
29
|
+
pending: {},
|
|
30
|
+
order: [],
|
|
31
|
+
activeId: null
|
|
32
|
+
};
|
|
33
|
+
const slices = {};
|
|
34
|
+
const reducers = {};
|
|
35
|
+
const changeListeners = /* @__PURE__ */ new Set();
|
|
36
|
+
const actionListeners = /* @__PURE__ */ new Set();
|
|
37
|
+
const guarded = (fn) => {
|
|
38
|
+
try {
|
|
39
|
+
fn();
|
|
40
|
+
} catch (error) {
|
|
41
|
+
report(error);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
let emitting = false;
|
|
45
|
+
let pending = false;
|
|
46
|
+
const emitChange = () => {
|
|
47
|
+
if (emitting) {
|
|
48
|
+
pending = true;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
emitting = true;
|
|
52
|
+
try {
|
|
53
|
+
do {
|
|
54
|
+
pending = false;
|
|
55
|
+
changeListeners.forEach((listener) => guarded(listener));
|
|
56
|
+
} while (pending);
|
|
57
|
+
} finally {
|
|
58
|
+
emitting = false;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const emitAction = (action) => actionListeners.forEach((listener) => guarded(() => listener(action)));
|
|
62
|
+
return {
|
|
63
|
+
registerSlice(key, reducer, initial) {
|
|
64
|
+
reducers[key] = reducer;
|
|
65
|
+
slices[key] = initial;
|
|
66
|
+
},
|
|
67
|
+
removeSlice(key) {
|
|
68
|
+
delete reducers[key];
|
|
69
|
+
delete slices[key];
|
|
70
|
+
emitChange();
|
|
71
|
+
},
|
|
72
|
+
getSlice: (key) => slices[key],
|
|
73
|
+
getCore: () => core,
|
|
74
|
+
getState: () => ({
|
|
75
|
+
core,
|
|
76
|
+
plugins: slices
|
|
77
|
+
}),
|
|
78
|
+
dispatchTo(key, action) {
|
|
79
|
+
const r = reducers[key];
|
|
80
|
+
if (!r) return;
|
|
81
|
+
const next = r(slices[key], action);
|
|
82
|
+
if (next !== slices[key]) {
|
|
83
|
+
slices[key] = next;
|
|
84
|
+
emitChange();
|
|
85
|
+
}
|
|
86
|
+
emitAction(action);
|
|
87
|
+
},
|
|
88
|
+
setCore(patch, action) {
|
|
89
|
+
core = {
|
|
90
|
+
...core,
|
|
91
|
+
...patch
|
|
92
|
+
};
|
|
93
|
+
emitChange();
|
|
94
|
+
emitAction(action);
|
|
95
|
+
},
|
|
96
|
+
subscribe(listener) {
|
|
97
|
+
changeListeners.add(listener);
|
|
98
|
+
return () => void changeListeners.delete(listener);
|
|
99
|
+
},
|
|
100
|
+
subscribeAction(listener) {
|
|
101
|
+
actionListeners.add(listener);
|
|
102
|
+
return () => void actionListeners.delete(listener);
|
|
103
|
+
},
|
|
104
|
+
destroy() {
|
|
105
|
+
core = {
|
|
106
|
+
documents: {},
|
|
107
|
+
pending: {},
|
|
108
|
+
order: [],
|
|
109
|
+
activeId: null
|
|
110
|
+
};
|
|
111
|
+
for (const key of Object.keys(slices)) delete slices[key];
|
|
112
|
+
for (const key of Object.keys(reducers)) delete reducers[key];
|
|
113
|
+
changeListeners.clear();
|
|
114
|
+
actionListeners.clear();
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region src/context.ts
|
|
120
|
+
/** A plugin's slice key. Workspace plugins use their id; document-scoped plugins are per-document. */
|
|
121
|
+
const sliceKey = (pluginId, documentId) => documentId ? `${pluginId}::${documentId}` : pluginId;
|
|
122
|
+
/**
|
|
123
|
+
* Build the context a plugin sees. When `session` is given the context is bound
|
|
124
|
+
* to that document — `getState`/`dispatch` target its slice, `document()` returns
|
|
125
|
+
* it, and `get()` resolves document-scoped capabilities for it.
|
|
126
|
+
*/
|
|
127
|
+
function createPluginContext(services, plugin, session) {
|
|
128
|
+
const { engine, store } = services;
|
|
129
|
+
const documentId = session?.id;
|
|
130
|
+
const key = sliceKey(plugin.id, documentId);
|
|
131
|
+
const ownScope = session?.scope ?? services.workspaceScope;
|
|
132
|
+
return {
|
|
133
|
+
id: plugin.id,
|
|
134
|
+
engine,
|
|
135
|
+
documentId,
|
|
136
|
+
doc: session ? session.handle : services.documentHandle(void 0),
|
|
137
|
+
documentHandle: (requestedDocumentId) => requestedDocumentId ? services.documentHandle(requestedDocumentId) : session ? session.handle : services.documentHandle(void 0),
|
|
138
|
+
getState: () => store.getSlice(key),
|
|
139
|
+
dispatch: (action) => store.dispatchTo(key, action),
|
|
140
|
+
subscribe: store.subscribe,
|
|
141
|
+
core: store.getCore,
|
|
142
|
+
document: () => {
|
|
143
|
+
if (session) return store.getCore().documents[session.id] ?? session.stagedMeta;
|
|
144
|
+
const id = store.getCore().activeId;
|
|
145
|
+
return id ? store.getCore().documents[id] ?? null : null;
|
|
146
|
+
},
|
|
147
|
+
get: (token) => services.resolveCapability(token, documentId),
|
|
148
|
+
forDocument: (token, otherDocumentId) => services.resolveCapability(token, otherDocumentId),
|
|
149
|
+
tryGet: (token) => services.tryResolveCapability(token, documentId),
|
|
150
|
+
cleanup: (teardown) => ownScope.defer(teardown)
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/** A plugin context plus the side-effect primitives (watch / onAction / cleanup). */
|
|
154
|
+
function createEffectContext(services, plugin, session) {
|
|
155
|
+
const { store } = services;
|
|
156
|
+
const ownScope = session?.scope ?? services.workspaceScope;
|
|
157
|
+
return {
|
|
158
|
+
...createPluginContext(services, plugin, session),
|
|
159
|
+
watch: (select, handler, isEqual = Object.is) => {
|
|
160
|
+
let previous = select();
|
|
161
|
+
const unsubscribe = store.subscribe(() => {
|
|
162
|
+
const next = select();
|
|
163
|
+
if (!isEqual(previous, next)) {
|
|
164
|
+
const prior = previous;
|
|
165
|
+
previous = next;
|
|
166
|
+
handler(next, prior);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
ownScope.defer(unsubscribe);
|
|
170
|
+
return unsubscribe;
|
|
171
|
+
},
|
|
172
|
+
onAction: (type, handler) => {
|
|
173
|
+
const unsubscribe = store.subscribeAction((action) => {
|
|
174
|
+
if (action.type === type) handler(action);
|
|
175
|
+
});
|
|
176
|
+
ownScope.defer(unsubscribe);
|
|
177
|
+
return unsubscribe;
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/order.ts
|
|
183
|
+
/**
|
|
184
|
+
* Validate `requires` (fail fast) and topologically sort plugins so dependencies
|
|
185
|
+
* are initialised before dependents.
|
|
186
|
+
*/
|
|
187
|
+
function planPlugins(plugins) {
|
|
188
|
+
const providerByToken = /* @__PURE__ */ new Map();
|
|
189
|
+
const scopeByToken = /* @__PURE__ */ new Map([[DocumentsToken, "workspace"]]);
|
|
190
|
+
for (const plugin of plugins) if (plugin.token && plugin.capability) {
|
|
191
|
+
providerByToken.set(plugin.token, plugin);
|
|
192
|
+
scopeByToken.set(plugin.token, plugin.scope ?? "workspace");
|
|
193
|
+
}
|
|
194
|
+
for (const plugin of plugins) for (const required of plugin.requires ?? []) if (required !== DocumentsToken && !providerByToken.has(required)) throw new Error(`Plugin "${plugin.id}" requires capability "${required.name}", which no plugin provides.`);
|
|
195
|
+
const ordered = [];
|
|
196
|
+
const status = /* @__PURE__ */ new Map();
|
|
197
|
+
const visit = (plugin) => {
|
|
198
|
+
const state = status.get(plugin.id);
|
|
199
|
+
if (state === "done") return;
|
|
200
|
+
if (state === "visiting") throw new Error(`Dependency cycle involving plugin "${plugin.id}".`);
|
|
201
|
+
status.set(plugin.id, "visiting");
|
|
202
|
+
for (const token of [...plugin.requires ?? [], ...plugin.optional ?? []]) {
|
|
203
|
+
const dependency = providerByToken.get(token);
|
|
204
|
+
if (dependency && dependency !== plugin) visit(dependency);
|
|
205
|
+
}
|
|
206
|
+
status.set(plugin.id, "done");
|
|
207
|
+
ordered.push(plugin);
|
|
208
|
+
};
|
|
209
|
+
for (const plugin of plugins) visit(plugin);
|
|
210
|
+
return {
|
|
211
|
+
ordered,
|
|
212
|
+
providerOf: (token) => providerByToken.get(token),
|
|
213
|
+
scopeOf: (token) => scopeByToken.get(token) ?? "workspace"
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
//#endregion
|
|
217
|
+
//#region src/scope.ts
|
|
218
|
+
/**
|
|
219
|
+
* A lifecycle operation was cancelled — the tab was closed mid-open, or the
|
|
220
|
+
* kernel was destroyed mid-start. NOT a failure: callers that race `open()`
|
|
221
|
+
* against `close()` should treat this rejection as "the close won".
|
|
222
|
+
*/
|
|
223
|
+
var CancelledError = class extends Error {
|
|
224
|
+
constructor(what) {
|
|
225
|
+
super(`[kernel] cancelled: ${what}`);
|
|
226
|
+
this.name = "CancelledError";
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
const isCancelled = (error) => error instanceof CancelledError;
|
|
230
|
+
function createScope(report) {
|
|
231
|
+
const teardowns = [];
|
|
232
|
+
let disposal = null;
|
|
233
|
+
return {
|
|
234
|
+
get disposed() {
|
|
235
|
+
return disposal !== null;
|
|
236
|
+
},
|
|
237
|
+
defer(teardown) {
|
|
238
|
+
if (disposal) disposal.then(() => Promise.resolve().then(teardown).catch(report));
|
|
239
|
+
else teardowns.push(teardown);
|
|
240
|
+
},
|
|
241
|
+
dispose() {
|
|
242
|
+
return disposal ?? (disposal = (async () => {
|
|
243
|
+
while (teardowns.length) try {
|
|
244
|
+
await teardowns.pop()();
|
|
245
|
+
} catch (error) {
|
|
246
|
+
report(error);
|
|
247
|
+
}
|
|
248
|
+
})());
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
//#endregion
|
|
253
|
+
//#region src/kernel.ts
|
|
254
|
+
/** The new page registry a document mutation carries, or null for events that
|
|
255
|
+
* don't change page structure (annotations, metadata). The snapshot is the
|
|
256
|
+
* same shape `pages.list()` returns, so callers swap it in directly. */
|
|
257
|
+
function layoutFromEvent(event) {
|
|
258
|
+
switch (event.type) {
|
|
259
|
+
case "pages.moved":
|
|
260
|
+
case "pages.rotated":
|
|
261
|
+
case "pages.deleted":
|
|
262
|
+
case "pages.inserted": return event.layout;
|
|
263
|
+
default: return null;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const isDocumentScoped = (plugin) => plugin.scope === "document";
|
|
267
|
+
const initialStateOf = (plugin) => typeof plugin.initialState === "function" ? plugin.initialState() : plugin.initialState ?? {};
|
|
268
|
+
const reducerOf = (plugin) => plugin.reduce ?? ((state) => state);
|
|
269
|
+
const toDocInfo = (meta) => ({
|
|
270
|
+
id: meta.id,
|
|
271
|
+
name: meta.name,
|
|
272
|
+
status: "ready",
|
|
273
|
+
pageCount: meta.pageCount
|
|
274
|
+
});
|
|
275
|
+
const pendingToDocInfo = (meta) => ({
|
|
276
|
+
id: meta.id,
|
|
277
|
+
name: meta.name,
|
|
278
|
+
status: meta.status,
|
|
279
|
+
pageCount: 0,
|
|
280
|
+
passwordProvided: meta.passwordProvided
|
|
281
|
+
});
|
|
282
|
+
/** The stable id an input implies, if it carries one ('bytes'/'layerBytes'/'id'). */
|
|
283
|
+
const idOfInput = (input) => "id" in input && typeof input.id === "string" ? input.id : null;
|
|
284
|
+
const passwordOfInput = (input) => "password" in input ? input.password : void 0;
|
|
285
|
+
/** Engine rejections carry AbortError when close() aborts the live call;
|
|
286
|
+
* match structurally so test fakes with plain promises still work. */
|
|
287
|
+
const isAbortLike = (error) => error instanceof Error && error.name === "AbortError";
|
|
288
|
+
/**
|
|
289
|
+
* Assemble a kernel from an engine + plugins.
|
|
290
|
+
*
|
|
291
|
+
* planPlugins — validate dependencies, order them
|
|
292
|
+
* resolveCapability — workspace singletons, or per-document instances built lazily
|
|
293
|
+
* document lifecycle — one DocumentSession per document: transactional open
|
|
294
|
+
* (publish-last), one idempotent close for every phase
|
|
295
|
+
* start / destroy — explicit status machine; destroy closes everything
|
|
296
|
+
*
|
|
297
|
+
* The kernel closes every handle it opened; it never destroys the engine —
|
|
298
|
+
* ownership follows acquisition, and the engine was handed in by the caller.
|
|
299
|
+
*/
|
|
300
|
+
function createKernel(opts) {
|
|
301
|
+
const { engine, plugins } = opts;
|
|
302
|
+
const report = opts.report ?? ((error) => console.error("[kernel]", error));
|
|
303
|
+
const store = createStore(report);
|
|
304
|
+
const plan = planPlugins(plugins);
|
|
305
|
+
const documentScopedPlugins = plan.ordered.filter(isDocumentScoped);
|
|
306
|
+
const workspaceCapabilities = /* @__PURE__ */ new Map();
|
|
307
|
+
const workspaceScope = createScope(report);
|
|
308
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
309
|
+
let status = "created";
|
|
310
|
+
let startPromise = null;
|
|
311
|
+
let destroyPromise = null;
|
|
312
|
+
const guardUsable = (what) => {
|
|
313
|
+
if (status === "failed" || status === "destroying" || status === "destroyed") throw new Error(`[kernel] ${what} on a ${status} kernel`);
|
|
314
|
+
};
|
|
315
|
+
const checkpoint = (session) => {
|
|
316
|
+
if (session.cancel.signal.aborted) throw new CancelledError(`closed while opening: ${session.id}`);
|
|
317
|
+
};
|
|
318
|
+
/**
|
|
319
|
+
* Await an engine/network call under the session's cancellation:
|
|
320
|
+
* - the call is retained so close() can abort real worker-side work
|
|
321
|
+
* (`AbortablePromise`), and
|
|
322
|
+
* - the await RACES the cancellation, so close()'s join never blocks on a
|
|
323
|
+
* call that cannot be aborted (a plain-promise engine, a stuck fetch).
|
|
324
|
+
* When cancellation wins but the call later lands anyway, `onLateResult`
|
|
325
|
+
* routes the result into the session scope — whose late-defer rule runs it
|
|
326
|
+
* immediately after disposal — so a late-arriving resource cannot leak.
|
|
327
|
+
* (Plugin inits are deliberately NOT raced: they are first-party code that
|
|
328
|
+
* close() joins to completion; only unbounded external waits are raced.)
|
|
329
|
+
*/
|
|
330
|
+
async function engineCall(session, call, onLateResult) {
|
|
331
|
+
const abortable = call;
|
|
332
|
+
session.engineOp = typeof abortable.abort === "function" ? abortable : null;
|
|
333
|
+
const signal = session.cancel.signal;
|
|
334
|
+
let onAbort;
|
|
335
|
+
const cancellation = new Promise((_, reject) => {
|
|
336
|
+
onAbort = () => reject(new CancelledError(`closed while opening: ${session.id}`));
|
|
337
|
+
if (signal.aborted) onAbort();
|
|
338
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
339
|
+
});
|
|
340
|
+
try {
|
|
341
|
+
return await Promise.race([call, cancellation]);
|
|
342
|
+
} catch (error) {
|
|
343
|
+
if (isCancelled(error)) call.then((value) => {
|
|
344
|
+
if (onLateResult) session.scope.defer(() => onLateResult(value));
|
|
345
|
+
}, () => {});
|
|
346
|
+
throw error;
|
|
347
|
+
} finally {
|
|
348
|
+
if (onAbort) signal.removeEventListener("abort", onAbort);
|
|
349
|
+
session.engineOp = null;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function createSession(id, name) {
|
|
353
|
+
const session = {
|
|
354
|
+
id,
|
|
355
|
+
name,
|
|
356
|
+
phase: "opening",
|
|
357
|
+
handle: null,
|
|
358
|
+
stagedMeta: null,
|
|
359
|
+
scope: createScope(report),
|
|
360
|
+
operation: null,
|
|
361
|
+
engineOp: null,
|
|
362
|
+
cancel: new AbortController(),
|
|
363
|
+
capabilities: /* @__PURE__ */ new Map(),
|
|
364
|
+
close: () => closeSession(session)
|
|
365
|
+
};
|
|
366
|
+
return session;
|
|
367
|
+
}
|
|
368
|
+
/** One transition at a time per session (open, then possibly unlock). */
|
|
369
|
+
function beginOperation(session, run) {
|
|
370
|
+
if (session.operation) throw new Error(`[documents] "${session.id}" already has an active transition`);
|
|
371
|
+
const operation = run().finally(() => {
|
|
372
|
+
if (session.operation === operation) session.operation = null;
|
|
373
|
+
});
|
|
374
|
+
session.operation = operation;
|
|
375
|
+
return operation;
|
|
376
|
+
}
|
|
377
|
+
/** Atomic ticket → real-id reconciliation: the sessions map, the store slot,
|
|
378
|
+
* order position, and activation all move together, or not at all. */
|
|
379
|
+
function rekeySession(session, nextId) {
|
|
380
|
+
const previousId = session.id;
|
|
381
|
+
if (previousId === nextId) return;
|
|
382
|
+
const core = store.getCore();
|
|
383
|
+
if (sessions.has(nextId) || core.documents[nextId] || core.pending[nextId]) throw new Error(`[documents] duplicate document id: ${nextId}`);
|
|
384
|
+
sessions.delete(previousId);
|
|
385
|
+
session.id = nextId;
|
|
386
|
+
sessions.set(nextId, session);
|
|
387
|
+
const slot = core.pending[previousId];
|
|
388
|
+
if (slot) {
|
|
389
|
+
const { [previousId]: _moved, ...pending } = core.pending;
|
|
390
|
+
store.setCore({
|
|
391
|
+
pending: {
|
|
392
|
+
...pending,
|
|
393
|
+
[nextId]: {
|
|
394
|
+
...slot,
|
|
395
|
+
id: nextId
|
|
396
|
+
}
|
|
397
|
+
},
|
|
398
|
+
order: core.order.map((id) => id === previousId ? nextId : id),
|
|
399
|
+
activeId: core.activeId === previousId ? nextId : core.activeId
|
|
400
|
+
}, { type: CORE_ORDER_CHANGED });
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
const closingSessions = /* @__PURE__ */ new WeakMap();
|
|
404
|
+
function closeSession(session) {
|
|
405
|
+
const inFlight = closingSessions.get(session);
|
|
406
|
+
if (inFlight) return inFlight;
|
|
407
|
+
const closing = (async () => {
|
|
408
|
+
session.phase = "closing";
|
|
409
|
+
unpublishSlot(session.id);
|
|
410
|
+
const reason = new CancelledError(`closed while opening: ${session.id}`);
|
|
411
|
+
session.cancel.abort(reason);
|
|
412
|
+
session.engineOp?.abort(reason);
|
|
413
|
+
await session.operation?.catch((error) => {
|
|
414
|
+
if (!isCancelled(error) && !isAbortLike(error)) report(error);
|
|
415
|
+
});
|
|
416
|
+
await session.scope.dispose();
|
|
417
|
+
if (sessions.get(session.id) === session) sessions.delete(session.id);
|
|
418
|
+
})();
|
|
419
|
+
closingSessions.set(session, closing);
|
|
420
|
+
return closing;
|
|
421
|
+
}
|
|
422
|
+
function nextActiveDocument(core, removedId) {
|
|
423
|
+
if (core.activeId !== removedId) return core.activeId;
|
|
424
|
+
const index = core.order.indexOf(removedId);
|
|
425
|
+
const remaining = core.order.filter((id) => id !== removedId);
|
|
426
|
+
return remaining.length === 0 ? null : remaining[Math.max(0, index - 1)] ?? remaining[0];
|
|
427
|
+
}
|
|
428
|
+
function publishPendingSlot(session, activate) {
|
|
429
|
+
const core = store.getCore();
|
|
430
|
+
store.setCore({
|
|
431
|
+
pending: {
|
|
432
|
+
...core.pending,
|
|
433
|
+
[session.id]: {
|
|
434
|
+
id: session.id,
|
|
435
|
+
name: session.name,
|
|
436
|
+
status: "loading"
|
|
437
|
+
}
|
|
438
|
+
},
|
|
439
|
+
order: [...core.order, session.id],
|
|
440
|
+
activeId: activate || core.activeId === null ? session.id : core.activeId
|
|
441
|
+
}, { type: CORE_DOCUMENT_OPENING });
|
|
442
|
+
}
|
|
443
|
+
function publishLocked(session, passwordProvided) {
|
|
444
|
+
const core = store.getCore();
|
|
445
|
+
store.setCore({ pending: {
|
|
446
|
+
...core.pending,
|
|
447
|
+
[session.id]: {
|
|
448
|
+
id: session.id,
|
|
449
|
+
name: session.name,
|
|
450
|
+
status: "locked",
|
|
451
|
+
passwordProvided
|
|
452
|
+
}
|
|
453
|
+
} }, { type: CORE_DOCUMENT_LOCKED });
|
|
454
|
+
}
|
|
455
|
+
function publishError(session, error) {
|
|
456
|
+
const core = store.getCore();
|
|
457
|
+
store.setCore({ pending: {
|
|
458
|
+
...core.pending,
|
|
459
|
+
[session.id]: {
|
|
460
|
+
id: session.id,
|
|
461
|
+
name: session.name,
|
|
462
|
+
status: "error",
|
|
463
|
+
error
|
|
464
|
+
}
|
|
465
|
+
} }, { type: CORE_DOCUMENT_OPEN_FAILED });
|
|
466
|
+
}
|
|
467
|
+
function unpublishSlot(id) {
|
|
468
|
+
const core = store.getCore();
|
|
469
|
+
if (!core.pending[id] && !core.documents[id]) return;
|
|
470
|
+
const { [id]: _pending, ...pending } = core.pending;
|
|
471
|
+
const { [id]: _document, ...documents } = core.documents;
|
|
472
|
+
store.setCore({
|
|
473
|
+
pending,
|
|
474
|
+
documents,
|
|
475
|
+
order: core.order.filter((other) => other !== id),
|
|
476
|
+
activeId: nextActiveDocument(core, id)
|
|
477
|
+
}, { type: CORE_DOCUMENT_REMOVED });
|
|
478
|
+
}
|
|
479
|
+
/** The ONE ready transition: swap the pending slot for the staged meta and
|
|
480
|
+
* fire CORE_DOCUMENT_ADDED. Everything before this is unpublished and rolls
|
|
481
|
+
* back by disposing the session scope; nothing after this can fail. */
|
|
482
|
+
function commitReady(session) {
|
|
483
|
+
session.phase = "ready";
|
|
484
|
+
const meta = session.stagedMeta;
|
|
485
|
+
const core = store.getCore();
|
|
486
|
+
const { [session.id]: _resolved, ...pending } = core.pending;
|
|
487
|
+
store.setCore({
|
|
488
|
+
documents: {
|
|
489
|
+
...core.documents,
|
|
490
|
+
[session.id]: meta
|
|
491
|
+
},
|
|
492
|
+
pending
|
|
493
|
+
}, { type: CORE_DOCUMENT_ADDED });
|
|
494
|
+
}
|
|
495
|
+
const sessionOf = (documentId) => {
|
|
496
|
+
const id = documentId ?? store.getCore().activeId;
|
|
497
|
+
return id ? sessions.get(id) ?? null : null;
|
|
498
|
+
};
|
|
499
|
+
/** The handle plugins may touch: bring-up or ready — never locked, never
|
|
500
|
+
* a handle whose close already won. */
|
|
501
|
+
const documentHandle = (documentId) => {
|
|
502
|
+
const session = sessionOf(documentId);
|
|
503
|
+
if (!session) return null;
|
|
504
|
+
return session.phase === "bringup" || session.phase === "ready" ? session.handle : null;
|
|
505
|
+
};
|
|
506
|
+
function buildDocumentCapability(plugin, session) {
|
|
507
|
+
let capability = session.capabilities.get(plugin);
|
|
508
|
+
if (!capability) {
|
|
509
|
+
capability = plugin.capability(createPluginContext(services, plugin, session));
|
|
510
|
+
session.capabilities.set(plugin, capability);
|
|
511
|
+
}
|
|
512
|
+
return capability;
|
|
513
|
+
}
|
|
514
|
+
function resolveCapability(token, documentId) {
|
|
515
|
+
guardUsable(`capability("${token.name}")`);
|
|
516
|
+
const workspaceCapability = workspaceCapabilities.get(token);
|
|
517
|
+
if (workspaceCapability) return workspaceCapability;
|
|
518
|
+
const provider = plan.providerOf(token);
|
|
519
|
+
if (!provider) throw new Error(`No capability "${token.name}".`);
|
|
520
|
+
const id = documentId ?? store.getCore().activeId;
|
|
521
|
+
if (!id) throw new Error(`Capability "${token.name}" requires an active document.`);
|
|
522
|
+
const session = sessions.get(id);
|
|
523
|
+
if (!session) throw new Error(`Capability "${token.name}" unavailable: no document "${id}".`);
|
|
524
|
+
if (session.phase === "bringup" || session.phase === "ready") return buildDocumentCapability(provider, session);
|
|
525
|
+
const shown = session.phase === "opening" ? "loading" : session.phase;
|
|
526
|
+
throw new Error(`Capability "${token.name}" unavailable: document "${id}" is ${shown}.`);
|
|
527
|
+
}
|
|
528
|
+
/** Internal total resolver: bring-up counts, so a plugin's `ctx.tryGet`
|
|
529
|
+
* works during its own document's init. */
|
|
530
|
+
function tryResolveInternal(token, documentId) {
|
|
531
|
+
if (status === "destroying" || status === "destroyed") return null;
|
|
532
|
+
const workspaceCapability = workspaceCapabilities.get(token);
|
|
533
|
+
if (workspaceCapability) return workspaceCapability;
|
|
534
|
+
const provider = plan.providerOf(token);
|
|
535
|
+
if (!provider) return null;
|
|
536
|
+
const session = sessionOf(documentId);
|
|
537
|
+
if (!session || session.phase !== "bringup" && session.phase !== "ready") return null;
|
|
538
|
+
return buildDocumentCapability(provider, session);
|
|
539
|
+
}
|
|
540
|
+
/** Public total resolver — see `Kernel.tryCapability`. `ready` only: the
|
|
541
|
+
* null→instance flip at commit time IS the adapters' re-render signal. */
|
|
542
|
+
function tryResolveCapability(token, documentId) {
|
|
543
|
+
if (status === "destroying" || status === "destroyed") return null;
|
|
544
|
+
const workspaceCapability = workspaceCapabilities.get(token);
|
|
545
|
+
if (workspaceCapability) return workspaceCapability;
|
|
546
|
+
const provider = plan.providerOf(token);
|
|
547
|
+
if (!provider) return null;
|
|
548
|
+
const session = sessionOf(documentId);
|
|
549
|
+
if (!session || session.phase !== "ready") return null;
|
|
550
|
+
return buildDocumentCapability(provider, session);
|
|
551
|
+
}
|
|
552
|
+
const services = {
|
|
553
|
+
engine,
|
|
554
|
+
store,
|
|
555
|
+
workspaceScope,
|
|
556
|
+
resolveCapability,
|
|
557
|
+
tryResolveCapability: tryResolveInternal,
|
|
558
|
+
documentHandle
|
|
559
|
+
};
|
|
560
|
+
let ticketCounter = 0;
|
|
561
|
+
const nextTicket = () => `pending:${++ticketCounter}`;
|
|
562
|
+
/** Slices + event subscription + plugin inits + effect SETUP — every step's
|
|
563
|
+
* release deferred into the session scope, every await followed by a
|
|
564
|
+
* checkpoint. Runs entirely pre-commit: a failure anywhere rolls the whole
|
|
565
|
+
* session back and the document was never `ready`. */
|
|
566
|
+
async function bringUp(session, snapshot) {
|
|
567
|
+
session.phase = "bringup";
|
|
568
|
+
let renderPolicy = _embedpdf_engine_core_runtime.CONTINUOUS_RENDER_POLICY;
|
|
569
|
+
try {
|
|
570
|
+
renderPolicy = await session.handle.render?.policy() ?? _embedpdf_engine_core_runtime.CONTINUOUS_RENDER_POLICY;
|
|
571
|
+
} catch {}
|
|
572
|
+
checkpoint(session);
|
|
573
|
+
session.stagedMeta = {
|
|
574
|
+
id: session.id,
|
|
575
|
+
name: session.name,
|
|
576
|
+
pageCount: snapshot.pageCount,
|
|
577
|
+
pages: snapshot.pages,
|
|
578
|
+
revision: 0,
|
|
579
|
+
renderPolicy
|
|
580
|
+
};
|
|
581
|
+
const unsubscribeEvents = session.handle.events.subscribe((event) => {
|
|
582
|
+
const layout = layoutFromEvent(event);
|
|
583
|
+
if (!layout) return;
|
|
584
|
+
const now = store.getCore();
|
|
585
|
+
const existing = now.documents[session.id];
|
|
586
|
+
if (!existing) return;
|
|
587
|
+
const updated = {
|
|
588
|
+
...existing,
|
|
589
|
+
pageCount: layout.pageCount,
|
|
590
|
+
pages: layout.pages,
|
|
591
|
+
revision: existing.revision + 1
|
|
592
|
+
};
|
|
593
|
+
store.setCore({ documents: {
|
|
594
|
+
...now.documents,
|
|
595
|
+
[session.id]: updated
|
|
596
|
+
} }, { type: CORE_DOCUMENT_PAGES_UPDATED });
|
|
597
|
+
});
|
|
598
|
+
session.scope.defer(unsubscribeEvents);
|
|
599
|
+
for (const plugin of documentScopedPlugins) {
|
|
600
|
+
const key = sliceKey(plugin.id, session.id);
|
|
601
|
+
store.registerSlice(key, reducerOf(plugin), initialStateOf(plugin));
|
|
602
|
+
session.scope.defer(() => store.removeSlice(key));
|
|
603
|
+
}
|
|
604
|
+
for (const plugin of documentScopedPlugins) {
|
|
605
|
+
await plugin.init?.(createPluginContext(services, plugin, session));
|
|
606
|
+
checkpoint(session);
|
|
607
|
+
}
|
|
608
|
+
for (const plugin of documentScopedPlugins) plugin.effects?.(createEffectContext(services, plugin, session));
|
|
609
|
+
checkpoint(session);
|
|
610
|
+
}
|
|
611
|
+
async function openDocument(input, options) {
|
|
612
|
+
guardUsable("documents.open()");
|
|
613
|
+
const { activate, name, ...engineOptions } = options ?? {};
|
|
614
|
+
const requestedId = typeof input === "function" ? nextTicket() : idOfInput(input) ?? nextTicket();
|
|
615
|
+
if (sessions.has(requestedId)) throw new Error(`[documents] document already open: ${requestedId}`);
|
|
616
|
+
const session = createSession(requestedId, name);
|
|
617
|
+
sessions.set(session.id, session);
|
|
618
|
+
publishPendingSlot(session, activate ?? true);
|
|
619
|
+
return beginOperation(session, async () => {
|
|
620
|
+
try {
|
|
621
|
+
const source = typeof input === "function" ? await engineCall(session, Promise.resolve(input(session.cancel.signal))) : input;
|
|
622
|
+
checkpoint(session);
|
|
623
|
+
const sourceId = idOfInput(source);
|
|
624
|
+
if (sourceId) rekeySession(session, sourceId);
|
|
625
|
+
const handle = await engineCall(session, engine.open(source, engineOptions), (late) => late.close());
|
|
626
|
+
session.handle = handle;
|
|
627
|
+
session.scope.defer(() => handle.close());
|
|
628
|
+
checkpoint(session);
|
|
629
|
+
if (handle.id !== session.id) rekeySession(session, handle.id);
|
|
630
|
+
if (handle.security?.passwordPrompt?.state === "required") {
|
|
631
|
+
const passwordProvided = "password" in engineOptions && engineOptions.password != null || passwordOfInput(source) != null;
|
|
632
|
+
session.phase = "locked";
|
|
633
|
+
publishLocked(session, passwordProvided);
|
|
634
|
+
return session.id;
|
|
635
|
+
}
|
|
636
|
+
const snapshot = await engineCall(session, handle.pages.list());
|
|
637
|
+
checkpoint(session);
|
|
638
|
+
await bringUp(session, snapshot);
|
|
639
|
+
commitReady(session);
|
|
640
|
+
return session.id;
|
|
641
|
+
} catch (error) {
|
|
642
|
+
if (isCancelled(error)) throw error;
|
|
643
|
+
if (session.phase === "closing" || isAbortLike(error)) throw new CancelledError(`closed while opening: ${session.id}`);
|
|
644
|
+
await session.scope.dispose();
|
|
645
|
+
session.phase = "error";
|
|
646
|
+
publishError(session, error);
|
|
647
|
+
throw error;
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
async function unlockDocument(id, input) {
|
|
652
|
+
guardUsable("documents.unlock()");
|
|
653
|
+
const session = sessions.get(id);
|
|
654
|
+
if (!session || session.phase !== "locked" || !session.handle) throw new Error(`[documents] document is not locked: ${id}`);
|
|
655
|
+
const handle = session.handle;
|
|
656
|
+
return beginOperation(session, async () => {
|
|
657
|
+
try {
|
|
658
|
+
await engineCall(session, handle.security.unlock({ password: input.password }));
|
|
659
|
+
} catch (error) {
|
|
660
|
+
if (session.phase === "closing" || isAbortLike(error)) throw new CancelledError(`closed while opening: ${session.id}`);
|
|
661
|
+
throw error;
|
|
662
|
+
}
|
|
663
|
+
checkpoint(session);
|
|
664
|
+
try {
|
|
665
|
+
const snapshot = await engineCall(session, handle.pages.list());
|
|
666
|
+
checkpoint(session);
|
|
667
|
+
await bringUp(session, snapshot);
|
|
668
|
+
commitReady(session);
|
|
669
|
+
} catch (error) {
|
|
670
|
+
if (isCancelled(error)) throw error;
|
|
671
|
+
if (session.phase === "closing" || isAbortLike(error)) throw new CancelledError(`closed while opening: ${session.id}`);
|
|
672
|
+
await session.scope.dispose();
|
|
673
|
+
session.phase = "error";
|
|
674
|
+
publishError(session, error);
|
|
675
|
+
throw error;
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
async function closeDocument(documentId) {
|
|
680
|
+
const session = sessions.get(documentId);
|
|
681
|
+
if (!session) return;
|
|
682
|
+
await session.close();
|
|
683
|
+
}
|
|
684
|
+
function reorder(next) {
|
|
685
|
+
store.setCore({ order: next }, { type: CORE_ORDER_CHANGED });
|
|
686
|
+
}
|
|
687
|
+
const documents = {
|
|
688
|
+
open: openDocument,
|
|
689
|
+
openAll: (docs) => {
|
|
690
|
+
guardUsable("documents.openAll()");
|
|
691
|
+
const activeIndex = Math.max(0, docs.findIndex((d) => d.active));
|
|
692
|
+
docs.forEach(({ source, active: _active, ...options }, index) => {
|
|
693
|
+
openDocument(source, {
|
|
694
|
+
...options,
|
|
695
|
+
activate: index === activeIndex
|
|
696
|
+
}).catch(() => {});
|
|
697
|
+
});
|
|
698
|
+
},
|
|
699
|
+
unlock: unlockDocument,
|
|
700
|
+
close: closeDocument,
|
|
701
|
+
closeAll: async () => {
|
|
702
|
+
for (const id of [...store.getCore().order]) await closeDocument(id);
|
|
703
|
+
},
|
|
704
|
+
setActive: (id) => {
|
|
705
|
+
const core = store.getCore();
|
|
706
|
+
if (core.documents[id] || core.pending[id]) store.setCore({ activeId: id }, { type: CORE_ACTIVE_CHANGED });
|
|
707
|
+
},
|
|
708
|
+
activeId: () => store.getCore().activeId,
|
|
709
|
+
list: () => store.getCore().order.map((id) => {
|
|
710
|
+
const core = store.getCore();
|
|
711
|
+
const meta = core.documents[id];
|
|
712
|
+
return meta ? toDocInfo(meta) : pendingToDocInfo(core.pending[id]);
|
|
713
|
+
}),
|
|
714
|
+
get: (id) => {
|
|
715
|
+
const core = store.getCore();
|
|
716
|
+
const meta = core.documents[id];
|
|
717
|
+
if (meta) return toDocInfo(meta);
|
|
718
|
+
const pending = core.pending[id];
|
|
719
|
+
return pending ? pendingToDocInfo(pending) : null;
|
|
720
|
+
},
|
|
721
|
+
has: (id) => {
|
|
722
|
+
const core = store.getCore();
|
|
723
|
+
return core.documents[id] !== void 0 || core.pending[id] !== void 0;
|
|
724
|
+
},
|
|
725
|
+
count: () => store.getCore().order.length,
|
|
726
|
+
order: () => [...store.getCore().order],
|
|
727
|
+
move: (id, toIndex) => {
|
|
728
|
+
const core = store.getCore();
|
|
729
|
+
if (!core.documents[id] && !core.pending[id]) return;
|
|
730
|
+
const without = core.order.filter((x) => x !== id);
|
|
731
|
+
const clamped = Math.max(0, Math.min(toIndex, without.length));
|
|
732
|
+
without.splice(clamped, 0, id);
|
|
733
|
+
reorder(without);
|
|
734
|
+
},
|
|
735
|
+
swap: (a, b) => {
|
|
736
|
+
const core = store.getCore();
|
|
737
|
+
const indexA = core.order.indexOf(a);
|
|
738
|
+
const indexB = core.order.indexOf(b);
|
|
739
|
+
if (indexA < 0 || indexB < 0) return;
|
|
740
|
+
const next = [...core.order];
|
|
741
|
+
next[indexA] = b;
|
|
742
|
+
next[indexB] = a;
|
|
743
|
+
reorder(next);
|
|
744
|
+
},
|
|
745
|
+
download: (id, opts) => {
|
|
746
|
+
const handle = documentHandle(id);
|
|
747
|
+
if (!handle) return Promise.reject(/* @__PURE__ */ new Error("[documents] no document to download"));
|
|
748
|
+
return handle.download(opts);
|
|
749
|
+
},
|
|
750
|
+
downloadLayer: (id) => {
|
|
751
|
+
const handle = documentHandle(id);
|
|
752
|
+
if (!handle) return Promise.reject(/* @__PURE__ */ new Error("[documents] no document to download"));
|
|
753
|
+
if (!handle.downloadLayer) return Promise.reject(/* @__PURE__ */ new Error("[documents] this engine cannot export a layer (open with a layer on the local engine)"));
|
|
754
|
+
return handle.downloadLayer();
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
workspaceCapabilities.set(DocumentsToken, documents);
|
|
758
|
+
for (const plugin of plan.ordered) if (!isDocumentScoped(plugin)) store.registerSlice(plugin.id, reducerOf(plugin), initialStateOf(plugin));
|
|
759
|
+
for (const plugin of plan.ordered) if (!isDocumentScoped(plugin) && plugin.token && plugin.capability) workspaceCapabilities.set(plugin.token, plugin.capability(createPluginContext(services, plugin)));
|
|
760
|
+
return {
|
|
761
|
+
engine,
|
|
762
|
+
documents,
|
|
763
|
+
capability: resolveCapability,
|
|
764
|
+
tryCapability: tryResolveCapability,
|
|
765
|
+
scopeOf: plan.scopeOf,
|
|
766
|
+
subscribe: store.subscribe,
|
|
767
|
+
getState: store.getState,
|
|
768
|
+
status: () => status,
|
|
769
|
+
start: () => {
|
|
770
|
+
if (status === "destroying" || status === "destroyed" || status === "failed") return Promise.reject(/* @__PURE__ */ new Error(`[kernel] start() on a ${status} kernel`));
|
|
771
|
+
return startPromise ?? (startPromise = (async () => {
|
|
772
|
+
status = "starting";
|
|
773
|
+
try {
|
|
774
|
+
for (const plugin of plan.ordered) {
|
|
775
|
+
if (status !== "starting") return;
|
|
776
|
+
if (!isDocumentScoped(plugin)) await plugin.init?.(createPluginContext(services, plugin));
|
|
777
|
+
}
|
|
778
|
+
if (status !== "starting") return;
|
|
779
|
+
for (const plugin of plan.ordered) if (!isDocumentScoped(plugin)) plugin.effects?.(createEffectContext(services, plugin));
|
|
780
|
+
status = "started";
|
|
781
|
+
} catch (error) {
|
|
782
|
+
status = "failed";
|
|
783
|
+
await workspaceScope.dispose();
|
|
784
|
+
workspaceCapabilities.clear();
|
|
785
|
+
throw error;
|
|
786
|
+
}
|
|
787
|
+
})());
|
|
788
|
+
},
|
|
789
|
+
destroy: () => {
|
|
790
|
+
return destroyPromise ?? (destroyPromise = (async () => {
|
|
791
|
+
const wasFailed = status === "failed";
|
|
792
|
+
status = "destroying";
|
|
793
|
+
await startPromise?.catch((error) => {
|
|
794
|
+
if (!isCancelled(error) && !wasFailed) report(error);
|
|
795
|
+
});
|
|
796
|
+
await Promise.allSettled([...sessions.values()].map((session) => session.close()));
|
|
797
|
+
sessions.clear();
|
|
798
|
+
await workspaceScope.dispose();
|
|
799
|
+
workspaceCapabilities.clear();
|
|
800
|
+
store.destroy();
|
|
801
|
+
status = "destroyed";
|
|
802
|
+
})());
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
//#endregion
|
|
807
|
+
//#region src/index.ts
|
|
808
|
+
/** Create a typed capability token. `name` is for debugging only. */
|
|
809
|
+
function createCapabilityToken(name) {
|
|
810
|
+
return { name };
|
|
811
|
+
}
|
|
812
|
+
/** Identity helper that pins a plugin's generics. The real win is inference. */
|
|
813
|
+
function definePlugin(def) {
|
|
814
|
+
return def;
|
|
815
|
+
}
|
|
816
|
+
//#endregion
|
|
817
|
+
Object.defineProperty(exports, "AbortablePromise", {
|
|
818
|
+
enumerable: true,
|
|
819
|
+
get: function() {
|
|
820
|
+
return _embedpdf_engine_core_runtime.AbortablePromise;
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
Object.defineProperty(exports, "CONTINUOUS_RENDER_POLICY", {
|
|
824
|
+
enumerable: true,
|
|
825
|
+
get: function() {
|
|
826
|
+
return _embedpdf_engine_core_runtime.CONTINUOUS_RENDER_POLICY;
|
|
827
|
+
}
|
|
828
|
+
});
|
|
829
|
+
exports.CORE_ACTIVE_CHANGED = CORE_ACTIVE_CHANGED;
|
|
830
|
+
exports.CORE_DOCUMENT_ADDED = CORE_DOCUMENT_ADDED;
|
|
831
|
+
exports.CORE_DOCUMENT_LOCKED = CORE_DOCUMENT_LOCKED;
|
|
832
|
+
exports.CORE_DOCUMENT_OPENING = CORE_DOCUMENT_OPENING;
|
|
833
|
+
exports.CORE_DOCUMENT_OPEN_FAILED = CORE_DOCUMENT_OPEN_FAILED;
|
|
834
|
+
exports.CORE_DOCUMENT_PAGES_UPDATED = CORE_DOCUMENT_PAGES_UPDATED;
|
|
835
|
+
exports.CORE_DOCUMENT_REMOVED = CORE_DOCUMENT_REMOVED;
|
|
836
|
+
exports.CORE_ORDER_CHANGED = CORE_ORDER_CHANGED;
|
|
837
|
+
exports.CancelledError = CancelledError;
|
|
838
|
+
exports.DocumentsToken = DocumentsToken;
|
|
839
|
+
exports.createCapabilityToken = createCapabilityToken;
|
|
840
|
+
exports.createKernel = createKernel;
|
|
841
|
+
exports.definePlugin = definePlugin;
|
|
842
|
+
exports.docInfoEquals = docInfoEquals;
|
|
843
|
+
exports.docInfoListEquals = docInfoListEquals;
|
|
844
|
+
exports.isCancelled = isCancelled;
|
|
845
|
+
Object.defineProperty(exports, "snapAppearanceScale", {
|
|
846
|
+
enumerable: true,
|
|
847
|
+
get: function() {
|
|
848
|
+
return _embedpdf_engine_core_runtime.snapAppearanceScale;
|
|
849
|
+
}
|
|
850
|
+
});
|
|
851
|
+
Object.defineProperty(exports, "snapFullPageViewport", {
|
|
852
|
+
enumerable: true,
|
|
853
|
+
get: function() {
|
|
854
|
+
return _embedpdf_engine_core_runtime.snapFullPageViewport;
|
|
855
|
+
}
|
|
856
|
+
});
|
|
857
|
+
Object.defineProperty(exports, "snapTileScale", {
|
|
858
|
+
enumerable: true,
|
|
859
|
+
get: function() {
|
|
860
|
+
return _embedpdf_engine_core_runtime.snapTileScale;
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
//# sourceMappingURL=index.cjs.map
|