diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..72e515e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,30 @@
+# Build and Release Folders
+bin-debug/
+bin-release/
+[Oo]bj/
+[Bb]in/
+
+# Other files and folders
+.settings/
+
+# Executables
+*.swf
+*.air
+*.ipa
+*.apk
+
+# Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties`
+# should NOT be excluded as they contain compiler settings and other important
+# information for Eclipse / Flash Builder.
+
+
+.venv/
+.logs/
+dist/
+build/
+
+
+*.png
+*.mp4
+*.mp3
+*.spec
\ No newline at end of file
diff --git a/.release/app.py b/.release/app.py
new file mode 100644
index 0000000..8cbc3bb
--- /dev/null
+++ b/.release/app.py
@@ -0,0 +1,2 @@
+import os
+print(12)
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..4b4de49
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,9 @@
+{
+ "editor.fontSize": 12,
+ "editor.formatOnSave": true,
+ "files.autoSave": "onFocusChange",
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "editor.wordWrap": "on",
+ "workbench.colorTheme": "VS Code Dark",
+ "editor.formatOnPaste": true
+}
diff --git a/aiortcToJs.py b/aiortcToJs.py
new file mode 100644
index 0000000..03860a7
--- /dev/null
+++ b/aiortcToJs.py
@@ -0,0 +1,89 @@
+"""
+尝试通过aiortc获取屏幕传递到前端js
+"""
+
+import webview
+import threading
+import time,os
+
+CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
+
+from module.record_screen_to_mp4 import ScreenVideoTrack,SystemAudioTrack
+
+# ---------------------- 1. 定义供 JS 调用的 Python 函数 ----------------------
+class Api:
+ """暴露给 JS 的 Python 接口类(所有方法都会被 JS 访问)"""
+ def __init__(self):
+ self.video_track = None
+ self.audio_track = None
+
+ def set_window(self, window):
+ """初始化时绑定窗口实例"""
+ self.window = window
+
+ async def fetch_media(self):
+ self.video_track = ScreenVideoTrack(fps=30, scale_factor=0.8)
+ self.audio_track = SystemAudioTrack(sample_rate=48000)
+ return self.video_track,self.audio_track
+
+ def python_hello(self, name):
+ """JS 调用的同步函数:接收参数,返回结果"""
+ print(f"[Python] 收到 JS 调用,参数:{name}")
+ return f"Hello {name}! 这是 Python 返回的消息"
+
+ def python_async_task(self, duration):
+ """JS 调用的异步函数:模拟耗时任务(不阻塞前端)"""
+ def async_task():
+ print(f"[Python] 开始异步任务,耗时 {duration} 秒")
+ time.sleep(duration)
+ # 异步任务完成后,主动调用 JS 函数通知结果
+ self.window.evaluate_js(f'js_receive_python_msg("异步任务完成!耗时 {duration} 秒")')
+ print(f"[Python] 异步任务结束")
+
+ # 启动子线程执行异步任务,避免阻塞 UI
+ threading.Thread(target=async_task, daemon=True).start()
+ return "异步任务已启动,请等待结果通知"
+
+ def python_close_window(self):
+ """JS 调用关闭窗口"""
+ print("[Python] 收到关闭窗口请求")
+ self.window.destroy()
+
+# ---------------------- 2. Python 主动调用 JS 函数 ----------------------
+def python_call_js_periodically(window):
+ """Python 定时调用 JS 函数(模拟主动推送数据)"""
+ count = 0
+ while window:
+ count += 1
+ # 执行 JS 函数,传递参数
+ window.evaluate_js(f'js_receive_python_msg("Python 主动推送:第 {count} 条消息")')
+ time.sleep(3) # 每 3 秒推送一次
+ if count >= 5:
+ break
+
+# ---------------------- 3. 启动窗口 ----------------------
+if __name__ == "__main__":
+ # 创建 API 实例
+ api = Api()
+
+ # 加载本地 HTML 文件(也可加载远程 URL:url="https://xxx.com")
+ window = webview.create_window(
+ title="PyWebView 双向通信示例",
+ # url="app_JsToPy.html", # 前端页面路径
+ url=f"file:///{CURRENT_DIR}/app.html",
+ resizable=True,
+ js_api=api # 关键:暴露 Python API 给 JS
+ )
+
+ # 绑定窗口实例到 API(供异步任务调用 JS)
+ api.set_window(window)
+
+ # 启动 Python 主动调用 JS 的线程(非阻塞)
+ threading.Thread(target=python_call_js_periodically, args=(window,), daemon=True).start()
+
+ # 运行窗口(阻塞主线程)
+ webview.start(
+ private_mode=False, # WebRTC必需关闭私有模式
+ debug=True, # 开发环境开启调试,生产环境关闭
+ http_server=True # 启用内置 HTTP 服务器(加载本地 HTML 必需)
+ )
\ No newline at end of file
diff --git a/app.html b/app.html
new file mode 100644
index 0000000..4e0be12
--- /dev/null
+++ b/app.html
@@ -0,0 +1,52 @@
+
+
+
+
+ 测试应用
+
+
+
+
+
+ 1111111111111
+
+
+
+
+
+
+
+
diff --git a/app.py b/app.py
new file mode 100644
index 0000000..21cd946
--- /dev/null
+++ b/app.py
@@ -0,0 +1,61 @@
+
+
+# 关键:确保Windows安装了WebView2运行时(pywebview2必需)
+# 下载地址:https://developer.microsoft.com/zh-CN/microsoft-edge/webview2/#download-section
+
+
+import sys,os,time
+import threading
+
+CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
+
+
+'''定义供 JS 调用的 Python 函数'''
+class Api:
+ """暴露给 JS 的 Python 接口类(所有方法都会被 JS 访问)"""
+ def __init__(self):
+ self.window = None # 保存窗口实例,用于 Python 主动调用 JS
+
+ def set_window(self, window):
+ """初始化时绑定窗口实例"""
+ self.window = window
+
+ def python_close_window(self):
+ """JS 调用关闭窗口"""
+ print("[Python] 收到关闭窗口请求")
+ self.window.destroy()
+
+'''Python 主动调用 JS 函数'''
+def python_call_js_periodically(window):
+ """Python 定时调用 JS 函数(模拟主动推送数据)"""
+ count = 0
+ while window:
+ count += 1
+ # 执行 JS 函数,传递参数
+ window.evaluate_js(f'js_receive_python_msg("Python主动推送:第{count}条消息")')
+ time.sleep(3) # 每 3 秒推送一次
+ if count >= 5:break
+
+import webview
+if __name__ == '__main__':
+ api = Api()
+
+ window = webview.create_window(
+ title='PyWebView', # 窗口标题
+ url=f"file:///{CURRENT_DIR}/app.html",
+ resizable=True, # 是否允许调整窗口大小(默认 True)
+ transparent=True,
+ # frameless=True,
+ # easy_drag=True, # 窗口无边框
+ js_api=api
+ )
+ # 绑定窗口实例到 API(供异步任务调用 JS)
+ api.set_window(window)
+
+ threading.Thread(target=python_call_js_periodically, args=(window,), daemon=True).start()
+
+ webview.start(
+ private_mode=False, # WebRTC必需关闭私有模式
+ # debug=True,
+ http_server=True
+ )
\ No newline at end of file
diff --git a/assets/alpine.min.js b/assets/alpine.min.js
new file mode 100644
index 0000000..10193aa
--- /dev/null
+++ b/assets/alpine.min.js
@@ -0,0 +1,8 @@
+/**
+ * Minified by jsDelivr using Terser v5.37.0.
+ * Original file: /gh/alpinejs/alpine@2.8.2/dist/alpine.js
+ *
+ * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
+ */
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).Alpine=t()}(this,(function(){"use strict";function e(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function t(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function n(n){for(var i=1;i tags. See https://github.com/alpinejs/alpine#${t}`):1!==e.content.childElementCount&&console.warn(`Alpine: tag with [${t}] encountered with an unexpected number of root elements. Make sure has a single root element. `)}function a(e){return e.toLowerCase().replace(/-(\w)/g,((e,t)=>t.toUpperCase()))}function l(e,t){if(!1===t(e))return;let n=e.firstElementChild;for(;n;)l(n,t),n=n.nextElementSibling}function c(e,t){var n;return function(){var i=this,r=arguments;clearTimeout(n),n=setTimeout((function(){n=null,e.apply(i,r)}),t)}}const u=(e,t,n)=>{if(console.warn(`Alpine Error: "${n}"\n\nExpression: "${t}"\nElement:`,e),!r())throw Object.assign(n,{el:e,expression:t}),n};function d(e,{el:t,expression:n}){try{const i=e();return i instanceof Promise?i.catch((e=>u(t,n,e))):i}catch(e){u(t,n,e)}}function f(e,t,n,i={}){return d((()=>"function"==typeof t?t.call(n):new Function(["$data",...Object.keys(i)],`var __alpine_result; with($data) { __alpine_result = ${t} }; return __alpine_result`)(n,...Object.values(i))),{el:e,expression:t})}const m=/^x-(on|bind|data|text|html|model|if|for|show|cloak|transition|ref|spread)\b/;function p(e){const t=y(e.name);return m.test(t)}function h(e,t,n){let i=Array.from(e.attributes).filter(p).map(v),r=i.filter((e=>"spread"===e.type))[0];if(r){let n=f(e,r.expression,t.$data);i=i.concat(Object.entries(n).map((([e,t])=>v({name:e,value:t}))))}return n?i.filter((e=>e.type===n)):function(e){let t=["bind","model","show","catch-all"];return e.sort(((e,n)=>{let i=-1===t.indexOf(e.type)?"catch-all":e.type,r=-1===t.indexOf(n.type)?"catch-all":n.type;return t.indexOf(i)-t.indexOf(r)}))}(i)}function v({name:e,value:t}){const n=y(e),i=n.match(m),r=n.match(/:([a-zA-Z0-9\-:]+)/),s=n.match(/\.[^.\]]+(?=[^\]]*$)/g)||[];return{type:i?i[1]:null,value:r?r[1]:null,modifiers:s.map((e=>e.replace(".",""))),expression:t}}function y(e){return e.startsWith("@")?e.replace("@","x-on:"):e.startsWith(":")?e.replace(":","x-bind:"):e}function b(e,t=Boolean){return e.split(" ").filter(t)}const g="in",x="out",_="cancelled";function w(e,t,n,i,r=!1){if(r)return t();if(e.__x_transition&&e.__x_transition.type===g)return;const s=h(e,i,"transition"),o=h(e,i,"show")[0];if(o&&o.modifiers.includes("transition")){let i=o.modifiers;if(i.includes("out")&&!i.includes("in"))return t();const r=i.includes("in")&&i.includes("out");i=r?i.filter(((e,t)=>t{}),i,r,g)}(e,i,t,n)}else s.some((e=>["enter","enter-start","enter-end"].includes(e.value)))?function(e,t,n,i,r){const s=b(A((n.find((e=>"enter"===e.value))||{expression:""}).expression,e,t)),o=b(A((n.find((e=>"enter-start"===e.value))||{expression:""}).expression,e,t)),a=b(A((n.find((e=>"enter-end"===e.value))||{expression:""}).expression,e,t));S(e,s,o,a,i,(()=>{}),g,r)}(e,i,s,t,n):t()}function E(e,t,n,i,r=!1){if(r)return t();if(e.__x_transition&&e.__x_transition.type===x)return;const s=h(e,i,"transition"),o=h(e,i,"show")[0];if(o&&o.modifiers.includes("transition")){let i=o.modifiers;if(i.includes("in")&&!i.includes("out"))return t();const r=i.includes("in")&&i.includes("out");i=r?i.filter(((e,t)=>t>i.indexOf("out"))):i,function(e,t,n,i,r){const s=n?O(t,"duration",150):O(t,"duration",150)/2,o={duration:s,origin:O(t,"origin","center"),first:{opacity:1,scale:100},second:{opacity:0,scale:O(t,"scale",95)}};k(e,t,(()=>{}),i,r,o,x)}(e,i,r,t,n)}else s.some((e=>["leave","leave-start","leave-end"].includes(e.value)))?function(e,t,n,i,r){const s=b(A((n.find((e=>"leave"===e.value))||{expression:""}).expression,e,t)),o=b(A((n.find((e=>"leave-start"===e.value))||{expression:""}).expression,e,t)),a=b(A((n.find((e=>"leave-end"===e.value))||{expression:""}).expression,e,t));S(e,s,o,a,(()=>{}),i,x,r)}(e,i,s,t,n):t()}function O(e,t,n){if(-1===e.indexOf(t))return n;const i=e[e.indexOf(t)+1];if(!i)return n;if("scale"===t&&!P(i))return n;if("duration"===t){let e=i.match(/([0-9]+)ms/);if(e)return e[1]}return"origin"===t&&["top","right","left","center","bottom"].includes(e[e.indexOf(t)+2])?[i,e[e.indexOf(t)+2]].join(" "):i}function k(e,t,n,i,r,s,o){e.__x_transition&&e.__x_transition.cancel&&e.__x_transition.cancel();const a=e.style.opacity,l=e.style.transform,c=e.style.transformOrigin,u=!t.includes("opacity")&&!t.includes("scale"),d=u||t.includes("opacity"),f=u||t.includes("scale"),m={start(){d&&(e.style.opacity=s.first.opacity),f&&(e.style.transform=`scale(${s.first.scale/100})`)},during(){f&&(e.style.transformOrigin=s.origin),e.style.transitionProperty=[d?"opacity":"",f?"transform":""].join(" ").trim(),e.style.transitionDuration=s.duration/1e3+"s",e.style.transitionTimingFunction="cubic-bezier(0.4, 0.0, 0.2, 1)"},show(){n()},end(){d&&(e.style.opacity=s.second.opacity),f&&(e.style.transform=`scale(${s.second.scale/100})`)},hide(){i()},cleanup(){d&&(e.style.opacity=a),f&&(e.style.transform=l),f&&(e.style.transformOrigin=c),e.style.transitionProperty=null,e.style.transitionDuration=null,e.style.transitionTimingFunction=null}};$(e,m,o,r)}const A=(e,t,n)=>"function"==typeof e?n.evaluateReturnExpression(t,e):e;function S(e,t,n,i,r,s,o,a){e.__x_transition&&e.__x_transition.cancel&&e.__x_transition.cancel();const l=e.__x_original_classes||[],c={start(){e.classList.add(...n)},during(){e.classList.add(...t)},show(){r()},end(){e.classList.remove(...n.filter((e=>!l.includes(e)))),e.classList.add(...i)},hide(){s()},cleanup(){e.classList.remove(...t.filter((e=>!l.includes(e)))),e.classList.remove(...i.filter((e=>!l.includes(e))))}};$(e,c,o,a)}function $(e,t,n,i){const r=C((()=>{t.hide(),e.isConnected&&t.cleanup(),delete e.__x_transition}));e.__x_transition={type:n,cancel:C((()=>{i(_),r()})),finish:r,nextFrame:null},t.start(),t.during(),e.__x_transition.nextFrame=requestAnimationFrame((()=>{let n=1e3*Number(getComputedStyle(e).transitionDuration.replace(/,.*/,"").replace("s",""));0===n&&(n=1e3*Number(getComputedStyle(e).animationDuration.replace("s",""))),t.show(),e.__x_transition.nextFrame=requestAnimationFrame((()=>{t.end(),setTimeout(e.__x_transition.finish,n)}))}))}function P(e){return!Array.isArray(e)&&!isNaN(e)}function C(e){let t=!1;return function(){t||(t=!0,e.apply(this,arguments))}}function j(e,t,i,r,s){o(t,"x-for");let a=D("function"==typeof i?e.evaluateReturnExpression(t,i):i),l=function(e,t,n,i){let r=h(t,e,"if")[0];if(r&&!e.evaluateReturnExpression(t,r.expression))return[];let s=e.evaluateReturnExpression(t,n.items,i);P(s)&&s>=0&&(s=Array.from(Array(s).keys(),(e=>e+1)));return s}(e,t,a,s),c=t;l.forEach(((i,o)=>{let u=function(e,t,i,r,s){let o=s?n({},s):{};o[e.item]=t,e.index&&(o[e.index]=i);e.collection&&(o[e.collection]=r);return o}(a,i,o,l,s()),d=function(e,t,n,i){let r=h(t,e,"bind").filter((e=>"key"===e.value))[0];return r?e.evaluateReturnExpression(t,r.expression,(()=>i)):n}(e,t,o,u),f=function(e,t){if(!e)return;if(void 0===e.__x_for_key)return;if(e.__x_for_key===t)return e;let n=e;for(;n;){if(n.__x_for_key===t)return n.parentElement.insertBefore(n,e);n=!(!n.nextElementSibling||void 0===n.nextElementSibling.__x_for_key)&&n.nextElementSibling}}(c.nextElementSibling,d);f?(delete f.__x_for_key,f.__x_for=u,e.updateElements(f,(()=>f.__x_for))):(f=function(e,t){let n=document.importNode(e.content,!0);return t.parentElement.insertBefore(n,t.nextElementSibling),t.nextElementSibling}(t,c),w(f,(()=>{}),(()=>{}),e,r),f.__x_for=u,e.initializeElements(f,(()=>f.__x_for))),c=f,c.__x_for_key=d})),function(e,t){var n=!(!e.nextElementSibling||void 0===e.nextElementSibling.__x_for_key)&&e.nextElementSibling;for(;n;){let e=n,i=n.nextElementSibling;E(n,(()=>{e.remove()}),(()=>{}),t),n=!(!i||void 0===i.__x_for_key)&&i}}(c,e)}function D(e){let t=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,n=String(e).match(/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/);if(!n)return;let i={};i.items=n[2].trim();let r=n[1].trim().replace(/^\(|\)$/g,""),s=r.match(t);return s?(i.item=r.replace(t,"").trim(),i.index=s[1].trim(),s[2]&&(i.collection=s[2].trim())):i.item=r,i}function T(e,t,n,r,o,l,c){var u=e.evaluateReturnExpression(t,r,o);if("value"===n){if(ge.ignoreFocusedForValueBinding&&document.activeElement.isSameNode(t))return;if(void 0===u&&String(r).match(/\./)&&(u=""),"radio"===t.type)void 0===t.attributes.value&&"bind"===l?t.value=u:"bind"!==l&&(t.checked=s(t.value,u));else if("checkbox"===t.type)"boolean"==typeof u||[null,void 0].includes(u)||"bind"!==l?"bind"!==l&&(Array.isArray(u)?t.checked=u.some((e=>s(e,t.value))):t.checked=!!u):t.value=String(u);else if("SELECT"===t.tagName)!function(e,t){const n=[].concat(t).map((e=>e+""));Array.from(e.options).forEach((e=>{e.selected=n.includes(e.value||e.text)}))}(t,u);else{if(t.value===u)return;t.value=u}}else if("class"===n)if(Array.isArray(u)){const e=t.__x_original_classes||[];t.setAttribute("class",i(e.concat(u)).join(" "))}else if("object"==typeof u){Object.keys(u).sort(((e,t)=>u[e]-u[t])).forEach((e=>{u[e]?b(e).forEach((e=>t.classList.add(e))):b(e).forEach((e=>t.classList.remove(e)))}))}else{const e=t.__x_original_classes||[],n=u?b(u):[];t.setAttribute("class",i(e.concat(n)).join(" "))}else n=c.includes("camel")?a(n):n,[null,void 0,!1].includes(u)?t.removeAttribute(n):!function(e){return["disabled","checked","required","readonly","hidden","open","selected","autofocus","itemscope","multiple","novalidate","allowfullscreen","allowpaymentrequest","formnovalidate","autoplay","controls","loop","muted","playsinline","default","ismap","reversed","async","defer","nomodule"].includes(e)}(n)?L(t,n,u):L(t,n,n)}function L(e,t,n){e.getAttribute(t)!=n&&e.setAttribute(t,n)}function N(e,t,n,i,r,s={}){const o={passive:i.includes("passive")};let l,u;if(i.includes("camel")&&(n=a(n)),i.includes("away")?(u=document,l=a=>{t.contains(a.target)||t.offsetWidth<1&&t.offsetHeight<1||(z(e,r,a,s),i.includes("once")&&document.removeEventListener(n,l,o))}):(u=i.includes("window")?window:i.includes("document")?document:t,l=a=>{if(u!==window&&u!==document||document.body.contains(t)){if(!(function(e){return["keydown","keyup"].includes(e)}(n)&&function(e,t){let n=t.filter((e=>!["window","document","prevent","stop"].includes(e)));if(n.includes("debounce")){let e=n.indexOf("debounce");n.splice(e,P((n[e+1]||"invalid-wait").split("ms")[0])?2:1)}if(0===n.length)return!1;if(1===n.length&&n[0]===R(e.key))return!1;const i=["ctrl","shift","alt","meta","cmd","super"].filter((e=>n.includes(e)));if(n=n.filter((e=>!i.includes(e))),i.length>0){if(i.filter((t=>("cmd"!==t&&"super"!==t||(t="meta"),e[`${t}Key`]))).length===i.length&&n[0]===R(e.key))return!1}return!0}(a,i)||(i.includes("prevent")&&a.preventDefault(),i.includes("stop")&&a.stopPropagation(),i.includes("self")&&a.target!==t))){z(e,r,a,s).then((e=>{!1===e?a.preventDefault():i.includes("once")&&u.removeEventListener(n,l,o)}))}}else u.removeEventListener(n,l,o)}),i.includes("debounce")){let e=i[i.indexOf("debounce")+1]||"invalid-wait",t=P(e.split("ms")[0])?Number(e.split("ms")[0]):250;l=c(l,t)}u.addEventListener(n,l,o)}function z(e,t,i,r){return e.evaluateCommandExpression(i.target,t,(()=>n(n({},r()),{},{$event:i})))}function R(e){switch(e){case"/":return"slash";case" ":case"Spacebar":return"space";default:return e&&e.replace(/([a-z])([A-Z])/g,"$1-$2").replace(/[_\s]/,"-").toLowerCase()}}function F(e,t,n){return"radio"===e.type&&(e.hasAttribute("name")||e.setAttribute("name",n)),(n,i)=>{if(n instanceof CustomEvent&&n.detail)return n.detail;if("checkbox"===e.type){if(Array.isArray(i)){const e=t.includes("number")?I(n.target.value):n.target.value;return n.target.checked?i.concat([e]):i.filter((t=>!s(t,e)))}return n.target.checked}if("select"===e.tagName.toLowerCase()&&e.multiple)return t.includes("number")?Array.from(n.target.selectedOptions).map((e=>I(e.value||e.text))):Array.from(n.target.selectedOptions).map((e=>e.value||e.text));{const e=n.target.value;return t.includes("number")?I(e):t.includes("trim")?e.trim():e}}}function I(e){const t=e?parseFloat(e):null;return P(t)?t:e}const{isArray:M}=Array,{getPrototypeOf:B,create:q,defineProperty:U,defineProperties:W,isExtensible:K,getOwnPropertyDescriptor:G,getOwnPropertyNames:H,getOwnPropertySymbols:V,preventExtensions:Z,hasOwnProperty:J}=Object,{push:Q,concat:X,map:Y}=Array.prototype;function ee(e){return void 0===e}function te(e){return"function"==typeof e}const ne=new WeakMap;function ie(e,t){ne.set(e,t)}const re=e=>ne.get(e)||e;function se(e,t){return e.valueIsObservable(t)?e.getProxy(t):t}function oe(e,t,n){X.call(H(n),V(n)).forEach((i=>{let r=G(n,i);r.configurable||(r=ve(e,r,se)),U(t,i,r)})),Z(t)}class ae{constructor(e,t){this.originalTarget=t,this.membrane=e}get(e,t){const{originalTarget:n,membrane:i}=this,r=n[t],{valueObserved:s}=i;return s(n,t),i.getProxy(r)}set(e,t,n){const{originalTarget:i,membrane:{valueMutated:r}}=this;return i[t]!==n?(i[t]=n,r(i,t)):"length"===t&&M(i)&&r(i,t),!0}deleteProperty(e,t){const{originalTarget:n,membrane:{valueMutated:i}}=this;return delete n[t],i(n,t),!0}apply(e,t,n){}construct(e,t,n){}has(e,t){const{originalTarget:n,membrane:{valueObserved:i}}=this;return i(n,t),t in n}ownKeys(e){const{originalTarget:t}=this;return X.call(H(t),V(t))}isExtensible(e){const t=K(e);if(!t)return t;const{originalTarget:n,membrane:i}=this,r=K(n);return r||oe(i,e,n),r}setPrototypeOf(e,t){}getPrototypeOf(e){const{originalTarget:t}=this;return B(t)}getOwnPropertyDescriptor(e,t){const{originalTarget:n,membrane:i}=this,{valueObserved:r}=this.membrane;r(n,t);let s=G(n,t);if(ee(s))return s;const o=G(e,t);return ee(o)?(s=ve(i,s,se),s.configurable||U(e,t,s),s):o}preventExtensions(e){const{originalTarget:t,membrane:n}=this;return oe(n,e,t),Z(t),!0}defineProperty(e,t,n){const{originalTarget:i,membrane:r}=this,{valueMutated:s}=r,{configurable:o}=n;if(J.call(n,"writable")&&!J.call(n,"value")){const e=G(i,t);n.value=e.value}return U(i,t,function(e){return J.call(e,"value")&&(e.value=re(e.value)),e}(n)),!1===o&&U(e,t,ve(r,n,se)),s(i,t),!0}}function le(e,t){return e.valueIsObservable(t)?e.getReadOnlyProxy(t):t}class ce{constructor(e,t){this.originalTarget=t,this.membrane=e}get(e,t){const{membrane:n,originalTarget:i}=this,r=i[t],{valueObserved:s}=n;return s(i,t),n.getReadOnlyProxy(r)}set(e,t,n){return!1}deleteProperty(e,t){return!1}apply(e,t,n){}construct(e,t,n){}has(e,t){const{originalTarget:n,membrane:{valueObserved:i}}=this;return i(n,t),t in n}ownKeys(e){const{originalTarget:t}=this;return X.call(H(t),V(t))}setPrototypeOf(e,t){}getOwnPropertyDescriptor(e,t){const{originalTarget:n,membrane:i}=this,{valueObserved:r}=i;r(n,t);let s=G(n,t);if(ee(s))return s;const o=G(e,t);return ee(o)?(s=ve(i,s,le),J.call(s,"set")&&(s.set=void 0),s.configurable||U(e,t,s),s):o}preventExtensions(e){return!1}defineProperty(e,t,n){return!1}}function ue(e){let t;return M(e)?t=[]:"object"==typeof e&&(t={}),t}const de=Object.prototype;function fe(e){if(null===e)return!1;if("object"!=typeof e)return!1;if(M(e))return!0;const t=B(e);return t===de||null===t||null===B(t)}const me=(e,t)=>{},pe=(e,t)=>{},he=e=>e;function ve(e,t,n){const{set:i,get:r}=t;return J.call(t,"value")?t.value=n(e,t.value):(ee(r)||(t.get=function(){return n(e,r.call(re(this)))}),ee(i)||(t.set=function(t){i.call(re(this),e.unwrapProxy(t))})),t}class ye{constructor(e){if(this.valueDistortion=he,this.valueMutated=pe,this.valueObserved=me,this.valueIsObservable=fe,this.objectGraph=new WeakMap,!ee(e)){const{valueDistortion:t,valueMutated:n,valueObserved:i,valueIsObservable:r}=e;this.valueDistortion=te(t)?t:he,this.valueMutated=te(n)?n:pe,this.valueObserved=te(i)?i:me,this.valueIsObservable=te(r)?r:fe}}getProxy(e){const t=re(e),n=this.valueDistortion(t);if(this.valueIsObservable(n)){const i=this.getReactiveState(t,n);return i.readOnly===e?e:i.reactive}return n}getReadOnlyProxy(e){e=re(e);const t=this.valueDistortion(e);return this.valueIsObservable(t)?this.getReactiveState(e,t).readOnly:t}unwrapProxy(e){return re(e)}getReactiveState(e,t){const{objectGraph:n}=this;let i=n.get(t);if(i)return i;const r=this;return i={get reactive(){const n=new ae(r,t),i=new Proxy(ue(t),n);return ie(i,e),U(this,"reactive",{value:i}),i},get readOnly(){const n=new ce(r,t),i=new Proxy(ue(t),n);return ie(i,e),U(this,"readOnly",{value:i}),i}},n.set(t,i),i}}class be{constructor(e,t=null){this.$el=e;const n=this.$el.getAttribute("x-data"),i=""===n?"{}":n,r=this.$el.getAttribute("x-init");let s={$el:this.$el},o=t?t.$el:this.$el;Object.entries(ge.magicProperties).forEach((([e,t])=>{Object.defineProperty(s,`$${e}`,{get:function(){return t(o)}})})),this.unobservedData=t?t.getUnobservedData():f(e,i,s);let{membrane:a,data:l}=this.wrapDataInObservable(this.unobservedData);var c;this.$data=l,this.membrane=a,this.unobservedData.$el=this.$el,this.unobservedData.$refs=this.getRefsProxy(),this.nextTickStack=[],this.unobservedData.$nextTick=e=>{this.nextTickStack.push(e)},this.watchers={},this.unobservedData.$watch=(e,t)=>{this.watchers[e]||(this.watchers[e]=[]),this.watchers[e].push(t)},Object.entries(ge.magicProperties).forEach((([e,t])=>{Object.defineProperty(this.unobservedData,`$${e}`,{get:function(){return t(o,this.$el)}})})),this.showDirectiveStack=[],this.showDirectiveLastElement,t||ge.onBeforeComponentInitializeds.forEach((e=>e(this))),r&&!t&&(this.pauseReactivity=!0,c=this.evaluateReturnExpression(this.$el,r),this.pauseReactivity=!1),this.initializeElements(this.$el,(()=>{}),t),this.listenForNewElementsToInitialize(),"function"==typeof c&&c.call(this.$data),t||setTimeout((()=>{ge.onComponentInitializeds.forEach((e=>e(this)))}),0)}getUnobservedData(){return function(e,t){let n=e.unwrapProxy(t),i={};return Object.keys(n).forEach((e=>{["$el","$refs","$nextTick","$watch"].includes(e)||(i[e]=n[e])})),i}(this.membrane,this.$data)}wrapDataInObservable(e){var t=this;let n=c((function(){t.updateElements(t.$el)}),0);return function(e,t){let n=new ye({valueMutated(e,n){t(e,n)}});return{data:n.getProxy(e),membrane:n}}(e,((e,i)=>{t.watchers[i]?t.watchers[i].forEach((t=>t(e[i]))):Array.isArray(e)?Object.keys(t.watchers).forEach((n=>{let r=n.split(".");"length"!==i&&r.reduce(((i,r)=>(Object.is(e,i[r])&&t.watchers[n].forEach((t=>t(e))),i[r])),t.unobservedData)})):Object.keys(t.watchers).filter((e=>e.includes("."))).forEach((n=>{let r=n.split(".");i===r[r.length-1]&&r.reduce(((r,s)=>(Object.is(e,r)&&t.watchers[n].forEach((t=>t(e[i]))),r[s])),t.unobservedData)})),t.pauseReactivity||n()}))}walkAndSkipNestedComponents(e,t,n=()=>{}){l(e,(e=>e.hasAttribute("x-data")&&!e.isSameNode(this.$el)?(e.__x||n(e),!1):t(e)))}initializeElements(e,t=()=>{},n=!1){this.walkAndSkipNestedComponents(e,(e=>void 0===e.__x_for_key&&(void 0===e.__x_inserted_me&&void this.initializeElement(e,t,!n))),(e=>{n||(e.__x=new be(e))})),this.executeAndClearRemainingShowDirectiveStack(),this.executeAndClearNextTickStack(e)}initializeElement(e,t,n=!0){e.hasAttribute("class")&&h(e,this).length>0&&(e.__x_original_classes=b(e.getAttribute("class"))),n&&this.registerListeners(e,t),this.resolveBoundAttributes(e,!0,t)}updateElements(e,t=()=>{}){this.walkAndSkipNestedComponents(e,(e=>{if(void 0!==e.__x_for_key&&!e.isSameNode(this.$el))return!1;this.updateElement(e,t)}),(e=>{e.__x=new be(e)})),this.executeAndClearRemainingShowDirectiveStack(),this.executeAndClearNextTickStack(e)}executeAndClearNextTickStack(e){e===this.$el&&this.nextTickStack.length>0&&requestAnimationFrame((()=>{for(;this.nextTickStack.length>0;)this.nextTickStack.shift()()}))}executeAndClearRemainingShowDirectiveStack(){this.showDirectiveStack.reverse().map((e=>new Promise(((t,n)=>{e(t,n)})))).reduce(((e,t)=>e.then((()=>t.then((e=>{e()}))))),Promise.resolve((()=>{}))).catch((e=>{if(e!==_)throw e})),this.showDirectiveStack=[],this.showDirectiveLastElement=void 0}updateElement(e,t){this.resolveBoundAttributes(e,!1,t)}registerListeners(e,t){h(e,this).forEach((({type:i,value:r,modifiers:s,expression:o})=>{switch(i){case"on":N(this,e,r,s,o,t);break;case"model":!function(e,t,i,r,s){var o="select"===t.tagName.toLowerCase()||["checkbox","radio"].includes(t.type)||i.includes("lazy")?"change":"input";N(e,t,o,i,`${r} = rightSideOfExpression($event, ${r})`,(()=>n(n({},s()),{},{rightSideOfExpression:F(t,i,r)})))}(this,e,s,o,t)}}))}resolveBoundAttributes(e,t=!1,n){let i=h(e,this);i.forEach((({type:r,value:s,modifiers:a,expression:l})=>{switch(r){case"model":T(this,e,"value",l,n,r,a);break;case"bind":if("template"===e.tagName.toLowerCase()&&"key"===s)return;T(this,e,s,l,n,r,a);break;case"text":var c=this.evaluateReturnExpression(e,l,n);!function(e,t,n){void 0===t&&String(n).match(/\./)&&(t=""),e.textContent=t}(e,c,l);break;case"html":!function(e,t,n,i){t.innerHTML=e.evaluateReturnExpression(t,n,i)}(this,e,l,n);break;case"show":c=this.evaluateReturnExpression(e,l,n);!function(e,t,n,i,r=!1){const s=()=>{t.style.display="none",t.__x_is_shown=!1},o=()=>{1===t.style.length&&"none"===t.style.display?t.removeAttribute("style"):t.style.removeProperty("display"),t.__x_is_shown=!0};if(!0===r)return void(n?o():s());const a=(i,r)=>{n?(("none"===t.style.display||t.__x_transition)&&w(t,(()=>{o()}),r,e),i((()=>{}))):"none"!==t.style.display?E(t,(()=>{i((()=>{s()}))}),r,e):i((()=>{}))};i.includes("immediate")?a((e=>e()),(()=>{})):(e.showDirectiveLastElement&&!e.showDirectiveLastElement.contains(t)&&e.executeAndClearRemainingShowDirectiveStack(),e.showDirectiveStack.push(a),e.showDirectiveLastElement=t)}(this,e,c,a,t);break;case"if":if(i.some((e=>"for"===e.type)))return;c=this.evaluateReturnExpression(e,l,n);!function(e,t,n,i,r){o(t,"x-if");const s=t.nextElementSibling&&!0===t.nextElementSibling.__x_inserted_me;if(!n||s&&!t.__x_transition)!n&&s&&E(t.nextElementSibling,(()=>{t.nextElementSibling.remove()}),(()=>{}),e,i);else{const n=document.importNode(t.content,!0);t.parentElement.insertBefore(n,t.nextElementSibling),w(t.nextElementSibling,(()=>{}),(()=>{}),e,i),e.initializeElements(t.nextElementSibling,r),t.nextElementSibling.__x_inserted_me=!0}}(this,e,c,t,n);break;case"for":j(this,e,l,t,n);break;case"cloak":e.removeAttribute("x-cloak")}}))}evaluateReturnExpression(e,t,i=()=>{}){return f(e,t,this.$data,n(n({},i()),{},{$dispatch:this.getDispatchFunction(e)}))}evaluateCommandExpression(e,t,i=()=>{}){return function(e,t,n,i={}){return d((()=>{if("function"==typeof t)return Promise.resolve(t.call(n,i.$event));let e=Function;if(e=Object.getPrototypeOf((async function(){})).constructor,Object.keys(n).includes(t)){let e=new Function(["dataContext",...Object.keys(i)],`with(dataContext) { return ${t} }`)(n,...Object.values(i));return"function"==typeof e?Promise.resolve(e.call(n,i.$event)):Promise.resolve()}return Promise.resolve(new e(["dataContext",...Object.keys(i)],`with(dataContext) { ${t} }`)(n,...Object.values(i)))}),{el:e,expression:t})}(e,t,this.$data,n(n({},i()),{},{$dispatch:this.getDispatchFunction(e)}))}getDispatchFunction(e){return(t,n={})=>{e.dispatchEvent(new CustomEvent(t,{detail:n,bubbles:!0}))}}listenForNewElementsToInitialize(){const e=this.$el;new MutationObserver((e=>{for(let t=0;t{this.$data[e]!==i[e]&&(this.$data[e]=i[e])}))}e[t].addedNodes.length>0&&e[t].addedNodes.forEach((e=>{1!==e.nodeType||e.__x_inserted_me||(!e.matches("[x-data]")||e.__x?this.initializeElements(e):e.__x=new be(e))}))}}})).observe(e,{childList:!0,attributes:!0,subtree:!0})}getRefsProxy(){var e=this;return new Proxy({},{get(t,n){return"$isAlpineProxy"===n||(e.walkAndSkipNestedComponents(e.$el,(e=>{e.hasAttribute("x-ref")&&e.getAttribute("x-ref")===n&&(i=e)})),i);var i}})}}const ge={version:"2.8.2",pauseMutationObserver:!1,magicProperties:{},onComponentInitializeds:[],onBeforeComponentInitializeds:[],ignoreFocusedForValueBinding:!1,start:async function(){r()||await new Promise((e=>{"loading"==document.readyState?document.addEventListener("DOMContentLoaded",e):e()})),this.discoverComponents((e=>{this.initializeComponent(e)})),document.addEventListener("turbolinks:load",(()=>{this.discoverUninitializedComponents((e=>{this.initializeComponent(e)}))})),this.listenForNewUninitializedComponentsAtRunTime()},discoverComponents:function(e){document.querySelectorAll("[x-data]").forEach((t=>{e(t)}))},discoverUninitializedComponents:function(e,t=null){const n=(t||document).querySelectorAll("[x-data]");Array.from(n).filter((e=>void 0===e.__x)).forEach((t=>{e(t)}))},listenForNewUninitializedComponentsAtRunTime:function(){const e=document.querySelector("body");new MutationObserver((e=>{if(!this.pauseMutationObserver)for(let t=0;t0&&e[t].addedNodes.forEach((e=>{1===e.nodeType&&(e.parentElement&&e.parentElement.closest("[x-data]")||this.discoverUninitializedComponents((e=>{this.initializeComponent(e)}),e.parentElement))}))})).observe(e,{childList:!0,attributes:!0,subtree:!0})},initializeComponent:function(e){if(!e.__x)try{e.__x=new be(e)}catch(e){setTimeout((()=>{throw e}),0)}},clone:function(e,t){t.__x||(t.__x=new be(t,e))},addMagicProperty:function(e,t){this.magicProperties[e]=t},onComponentInitialized:function(e){this.onComponentInitializeds.push(e)},onBeforeComponentInitialized:function(e){this.onBeforeComponentInitializeds.push(e)}};return r()||(window.Alpine=ge,window.deferLoadingAlpine?window.deferLoadingAlpine((function(){window.Alpine.start()})):window.Alpine.start()),ge}));
+//# sourceMappingURL=/sm/af874ca87b6c30c8cc5030246cb569336a69c56067e11b852d37f0680bdc2e07.map
\ No newline at end of file
diff --git a/assets/dexie.js b/assets/dexie.js
new file mode 100644
index 0000000..77c4851
--- /dev/null
+++ b/assets/dexie.js
@@ -0,0 +1,6016 @@
+/*
+ * Dexie.js - a minimalistic wrapper for IndexedDB
+ * ===============================================
+ *
+ * By David Fahlander, david.fahlander@gmail.com
+ *
+ * Version 4.2.1, Sat Oct 04 2025
+ *
+ * https://dexie.org
+ *
+ * Apache License Version 2.0, January 2004, http://www.apache.org/licenses/
+ */
+
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Dexie = factory());
+})(this, (function () { 'use strict';
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ var extendStatics = function(d, b) {
+ extendStatics = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
+ return extendStatics(d, b);
+ };
+ function __extends(d, b) {
+ if (typeof b !== "function" && b !== null)
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
+ extendStatics(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+ var __assign = function() {
+ __assign = Object.assign || function __assign(t) {
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
+ s = arguments[i];
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+ }
+ return t;
+ };
+ return __assign.apply(this, arguments);
+ };
+ function __spreadArray(to, from, pack) {
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
+ if (ar || !(i in from)) {
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
+ ar[i] = from[i];
+ }
+ }
+ return to.concat(ar || Array.prototype.slice.call(from));
+ }
+
+ var _global = typeof globalThis !== 'undefined' ? globalThis :
+ typeof self !== 'undefined' ? self :
+ typeof window !== 'undefined' ? window :
+ global;
+
+ var keys = Object.keys;
+ var isArray = Array.isArray;
+ if (typeof Promise !== 'undefined' && !_global.Promise) {
+ _global.Promise = Promise;
+ }
+ function extend(obj, extension) {
+ if (typeof extension !== 'object')
+ return obj;
+ keys(extension).forEach(function (key) {
+ obj[key] = extension[key];
+ });
+ return obj;
+ }
+ var getProto = Object.getPrototypeOf;
+ var _hasOwn = {}.hasOwnProperty;
+ function hasOwn(obj, prop) {
+ return _hasOwn.call(obj, prop);
+ }
+ function props(proto, extension) {
+ if (typeof extension === 'function')
+ extension = extension(getProto(proto));
+ (typeof Reflect === "undefined" ? keys : Reflect.ownKeys)(extension).forEach(function (key) {
+ setProp(proto, key, extension[key]);
+ });
+ }
+ var defineProperty = Object.defineProperty;
+ function setProp(obj, prop, functionOrGetSet, options) {
+ defineProperty(obj, prop, extend(functionOrGetSet && hasOwn(functionOrGetSet, "get") && typeof functionOrGetSet.get === 'function' ?
+ { get: functionOrGetSet.get, set: functionOrGetSet.set, configurable: true } :
+ { value: functionOrGetSet, configurable: true, writable: true }, options));
+ }
+ function derive(Child) {
+ return {
+ from: function (Parent) {
+ Child.prototype = Object.create(Parent.prototype);
+ setProp(Child.prototype, "constructor", Child);
+ return {
+ extend: props.bind(null, Child.prototype)
+ };
+ }
+ };
+ }
+ var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
+ function getPropertyDescriptor(obj, prop) {
+ var pd = getOwnPropertyDescriptor(obj, prop);
+ var proto;
+ return pd || (proto = getProto(obj)) && getPropertyDescriptor(proto, prop);
+ }
+ var _slice = [].slice;
+ function slice(args, start, end) {
+ return _slice.call(args, start, end);
+ }
+ function override(origFunc, overridedFactory) {
+ return overridedFactory(origFunc);
+ }
+ function assert(b) {
+ if (!b)
+ throw new Error("Assertion Failed");
+ }
+ function asap$1(fn) {
+ if (_global.setImmediate)
+ setImmediate(fn);
+ else
+ setTimeout(fn, 0);
+ }
+ function arrayToObject(array, extractor) {
+ return array.reduce(function (result, item, i) {
+ var nameAndValue = extractor(item, i);
+ if (nameAndValue)
+ result[nameAndValue[0]] = nameAndValue[1];
+ return result;
+ }, {});
+ }
+ function getByKeyPath(obj, keyPath) {
+ if (typeof keyPath === 'string' && hasOwn(obj, keyPath))
+ return obj[keyPath];
+ if (!keyPath)
+ return obj;
+ if (typeof keyPath !== 'string') {
+ var rv = [];
+ for (var i = 0, l = keyPath.length; i < l; ++i) {
+ var val = getByKeyPath(obj, keyPath[i]);
+ rv.push(val);
+ }
+ return rv;
+ }
+ var period = keyPath.indexOf('.');
+ if (period !== -1) {
+ var innerObj = obj[keyPath.substr(0, period)];
+ return innerObj == null ? undefined : getByKeyPath(innerObj, keyPath.substr(period + 1));
+ }
+ return undefined;
+ }
+ function setByKeyPath(obj, keyPath, value) {
+ if (!obj || keyPath === undefined)
+ return;
+ if ('isFrozen' in Object && Object.isFrozen(obj))
+ return;
+ if (typeof keyPath !== 'string' && 'length' in keyPath) {
+ assert(typeof value !== 'string' && 'length' in value);
+ for (var i = 0, l = keyPath.length; i < l; ++i) {
+ setByKeyPath(obj, keyPath[i], value[i]);
+ }
+ }
+ else {
+ var period = keyPath.indexOf('.');
+ if (period !== -1) {
+ var currentKeyPath = keyPath.substr(0, period);
+ var remainingKeyPath = keyPath.substr(period + 1);
+ if (remainingKeyPath === "")
+ if (value === undefined) {
+ if (isArray(obj) && !isNaN(parseInt(currentKeyPath)))
+ obj.splice(currentKeyPath, 1);
+ else
+ delete obj[currentKeyPath];
+ }
+ else
+ obj[currentKeyPath] = value;
+ else {
+ var innerObj = obj[currentKeyPath];
+ if (!innerObj || !hasOwn(obj, currentKeyPath))
+ innerObj = (obj[currentKeyPath] = {});
+ setByKeyPath(innerObj, remainingKeyPath, value);
+ }
+ }
+ else {
+ if (value === undefined) {
+ if (isArray(obj) && !isNaN(parseInt(keyPath)))
+ obj.splice(keyPath, 1);
+ else
+ delete obj[keyPath];
+ }
+ else
+ obj[keyPath] = value;
+ }
+ }
+ }
+ function delByKeyPath(obj, keyPath) {
+ if (typeof keyPath === 'string')
+ setByKeyPath(obj, keyPath, undefined);
+ else if ('length' in keyPath)
+ [].map.call(keyPath, function (kp) {
+ setByKeyPath(obj, kp, undefined);
+ });
+ }
+ function shallowClone(obj) {
+ var rv = {};
+ for (var m in obj) {
+ if (hasOwn(obj, m))
+ rv[m] = obj[m];
+ }
+ return rv;
+ }
+ var concat = [].concat;
+ function flatten(a) {
+ return concat.apply([], a);
+ }
+ var intrinsicTypeNames = "BigUint64Array,BigInt64Array,Array,Boolean,String,Date,RegExp,Blob,File,FileList,FileSystemFileHandle,FileSystemDirectoryHandle,ArrayBuffer,DataView,Uint8ClampedArray,ImageBitmap,ImageData,Map,Set,CryptoKey"
+ .split(',').concat(flatten([8, 16, 32, 64].map(function (num) { return ["Int", "Uint", "Float"].map(function (t) { return t + num + "Array"; }); }))).filter(function (t) { return _global[t]; });
+ var intrinsicTypes = new Set(intrinsicTypeNames.map(function (t) { return _global[t]; }));
+ function cloneSimpleObjectTree(o) {
+ var rv = {};
+ for (var k in o)
+ if (hasOwn(o, k)) {
+ var v = o[k];
+ rv[k] = !v || typeof v !== 'object' || intrinsicTypes.has(v.constructor) ? v : cloneSimpleObjectTree(v);
+ }
+ return rv;
+ }
+ function objectIsEmpty(o) {
+ for (var k in o)
+ if (hasOwn(o, k))
+ return false;
+ return true;
+ }
+ var circularRefs = null;
+ function deepClone(any) {
+ circularRefs = new WeakMap();
+ var rv = innerDeepClone(any);
+ circularRefs = null;
+ return rv;
+ }
+ function innerDeepClone(x) {
+ if (!x || typeof x !== 'object')
+ return x;
+ var rv = circularRefs.get(x);
+ if (rv)
+ return rv;
+ if (isArray(x)) {
+ rv = [];
+ circularRefs.set(x, rv);
+ for (var i = 0, l = x.length; i < l; ++i) {
+ rv.push(innerDeepClone(x[i]));
+ }
+ }
+ else if (intrinsicTypes.has(x.constructor)) {
+ rv = x;
+ }
+ else {
+ var proto = getProto(x);
+ rv = proto === Object.prototype ? {} : Object.create(proto);
+ circularRefs.set(x, rv);
+ for (var prop in x) {
+ if (hasOwn(x, prop)) {
+ rv[prop] = innerDeepClone(x[prop]);
+ }
+ }
+ }
+ return rv;
+ }
+ var toString = {}.toString;
+ function toStringTag(o) {
+ return toString.call(o).slice(8, -1);
+ }
+ var iteratorSymbol = typeof Symbol !== 'undefined' ?
+ Symbol.iterator :
+ '@@iterator';
+ var getIteratorOf = typeof iteratorSymbol === "symbol" ? function (x) {
+ var i;
+ return x != null && (i = x[iteratorSymbol]) && i.apply(x);
+ } : function () { return null; };
+ function delArrayItem(a, x) {
+ var i = a.indexOf(x);
+ if (i >= 0)
+ a.splice(i, 1);
+ return i >= 0;
+ }
+ var NO_CHAR_ARRAY = {};
+ function getArrayOf(arrayLike) {
+ var i, a, x, it;
+ if (arguments.length === 1) {
+ if (isArray(arrayLike))
+ return arrayLike.slice();
+ if (this === NO_CHAR_ARRAY && typeof arrayLike === 'string')
+ return [arrayLike];
+ if ((it = getIteratorOf(arrayLike))) {
+ a = [];
+ while ((x = it.next()), !x.done)
+ a.push(x.value);
+ return a;
+ }
+ if (arrayLike == null)
+ return [arrayLike];
+ i = arrayLike.length;
+ if (typeof i === 'number') {
+ a = new Array(i);
+ while (i--)
+ a[i] = arrayLike[i];
+ return a;
+ }
+ return [arrayLike];
+ }
+ i = arguments.length;
+ a = new Array(i);
+ while (i--)
+ a[i] = arguments[i];
+ return a;
+ }
+ var isAsyncFunction = typeof Symbol !== 'undefined'
+ ? function (fn) { return fn[Symbol.toStringTag] === 'AsyncFunction'; }
+ : function () { return false; };
+
+ var dexieErrorNames = [
+ 'Modify',
+ 'Bulk',
+ 'OpenFailed',
+ 'VersionChange',
+ 'Schema',
+ 'Upgrade',
+ 'InvalidTable',
+ 'MissingAPI',
+ 'NoSuchDatabase',
+ 'InvalidArgument',
+ 'SubTransaction',
+ 'Unsupported',
+ 'Internal',
+ 'DatabaseClosed',
+ 'PrematureCommit',
+ 'ForeignAwait'
+ ];
+ var idbDomErrorNames = [
+ 'Unknown',
+ 'Constraint',
+ 'Data',
+ 'TransactionInactive',
+ 'ReadOnly',
+ 'Version',
+ 'NotFound',
+ 'InvalidState',
+ 'InvalidAccess',
+ 'Abort',
+ 'Timeout',
+ 'QuotaExceeded',
+ 'Syntax',
+ 'DataClone'
+ ];
+ var errorList = dexieErrorNames.concat(idbDomErrorNames);
+ var defaultTexts = {
+ VersionChanged: "Database version changed by other database connection",
+ DatabaseClosed: "Database has been closed",
+ Abort: "Transaction aborted",
+ TransactionInactive: "Transaction has already completed or failed",
+ MissingAPI: "IndexedDB API missing. Please visit https://tinyurl.com/y2uuvskb"
+ };
+ function DexieError(name, msg) {
+ this.name = name;
+ this.message = msg;
+ }
+ derive(DexieError).from(Error).extend({
+ toString: function () { return this.name + ": " + this.message; }
+ });
+ function getMultiErrorMessage(msg, failures) {
+ return msg + ". Errors: " + Object.keys(failures)
+ .map(function (key) { return failures[key].toString(); })
+ .filter(function (v, i, s) { return s.indexOf(v) === i; })
+ .join('\n');
+ }
+ function ModifyError(msg, failures, successCount, failedKeys) {
+ this.failures = failures;
+ this.failedKeys = failedKeys;
+ this.successCount = successCount;
+ this.message = getMultiErrorMessage(msg, failures);
+ }
+ derive(ModifyError).from(DexieError);
+ function BulkError(msg, failures) {
+ this.name = "BulkError";
+ this.failures = Object.keys(failures).map(function (pos) { return failures[pos]; });
+ this.failuresByPos = failures;
+ this.message = getMultiErrorMessage(msg, this.failures);
+ }
+ derive(BulkError).from(DexieError);
+ var errnames = errorList.reduce(function (obj, name) { return (obj[name] = name + "Error", obj); }, {});
+ var BaseException = DexieError;
+ var exceptions = errorList.reduce(function (obj, name) {
+ var fullName = name + "Error";
+ function DexieError(msgOrInner, inner) {
+ this.name = fullName;
+ if (!msgOrInner) {
+ this.message = defaultTexts[name] || fullName;
+ this.inner = null;
+ }
+ else if (typeof msgOrInner === 'string') {
+ this.message = "".concat(msgOrInner).concat(!inner ? '' : '\n ' + inner);
+ this.inner = inner || null;
+ }
+ else if (typeof msgOrInner === 'object') {
+ this.message = "".concat(msgOrInner.name, " ").concat(msgOrInner.message);
+ this.inner = msgOrInner;
+ }
+ }
+ derive(DexieError).from(BaseException);
+ obj[name] = DexieError;
+ return obj;
+ }, {});
+ exceptions.Syntax = SyntaxError;
+ exceptions.Type = TypeError;
+ exceptions.Range = RangeError;
+ var exceptionMap = idbDomErrorNames.reduce(function (obj, name) {
+ obj[name + "Error"] = exceptions[name];
+ return obj;
+ }, {});
+ function mapError(domError, message) {
+ if (!domError || domError instanceof DexieError || domError instanceof TypeError || domError instanceof SyntaxError || !domError.name || !exceptionMap[domError.name])
+ return domError;
+ var rv = new exceptionMap[domError.name](message || domError.message, domError);
+ if ("stack" in domError) {
+ setProp(rv, "stack", { get: function () {
+ return this.inner.stack;
+ } });
+ }
+ return rv;
+ }
+ var fullNameExceptions = errorList.reduce(function (obj, name) {
+ if (["Syntax", "Type", "Range"].indexOf(name) === -1)
+ obj[name + "Error"] = exceptions[name];
+ return obj;
+ }, {});
+ fullNameExceptions.ModifyError = ModifyError;
+ fullNameExceptions.DexieError = DexieError;
+ fullNameExceptions.BulkError = BulkError;
+
+ function nop() { }
+ function mirror(val) { return val; }
+ function pureFunctionChain(f1, f2) {
+ if (f1 == null || f1 === mirror)
+ return f2;
+ return function (val) {
+ return f2(f1(val));
+ };
+ }
+ function callBoth(on1, on2) {
+ return function () {
+ on1.apply(this, arguments);
+ on2.apply(this, arguments);
+ };
+ }
+ function hookCreatingChain(f1, f2) {
+ if (f1 === nop)
+ return f2;
+ return function () {
+ var res = f1.apply(this, arguments);
+ if (res !== undefined)
+ arguments[0] = res;
+ var onsuccess = this.onsuccess,
+ onerror = this.onerror;
+ this.onsuccess = null;
+ this.onerror = null;
+ var res2 = f2.apply(this, arguments);
+ if (onsuccess)
+ this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess;
+ if (onerror)
+ this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror;
+ return res2 !== undefined ? res2 : res;
+ };
+ }
+ function hookDeletingChain(f1, f2) {
+ if (f1 === nop)
+ return f2;
+ return function () {
+ f1.apply(this, arguments);
+ var onsuccess = this.onsuccess,
+ onerror = this.onerror;
+ this.onsuccess = this.onerror = null;
+ f2.apply(this, arguments);
+ if (onsuccess)
+ this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess;
+ if (onerror)
+ this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror;
+ };
+ }
+ function hookUpdatingChain(f1, f2) {
+ if (f1 === nop)
+ return f2;
+ return function (modifications) {
+ var res = f1.apply(this, arguments);
+ extend(modifications, res);
+ var onsuccess = this.onsuccess,
+ onerror = this.onerror;
+ this.onsuccess = null;
+ this.onerror = null;
+ var res2 = f2.apply(this, arguments);
+ if (onsuccess)
+ this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess;
+ if (onerror)
+ this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror;
+ return res === undefined ?
+ (res2 === undefined ? undefined : res2) :
+ (extend(res, res2));
+ };
+ }
+ function reverseStoppableEventChain(f1, f2) {
+ if (f1 === nop)
+ return f2;
+ return function () {
+ if (f2.apply(this, arguments) === false)
+ return false;
+ return f1.apply(this, arguments);
+ };
+ }
+ function promisableChain(f1, f2) {
+ if (f1 === nop)
+ return f2;
+ return function () {
+ var res = f1.apply(this, arguments);
+ if (res && typeof res.then === 'function') {
+ var thiz = this, i = arguments.length, args = new Array(i);
+ while (i--)
+ args[i] = arguments[i];
+ return res.then(function () {
+ return f2.apply(thiz, args);
+ });
+ }
+ return f2.apply(this, arguments);
+ };
+ }
+
+ var debug = typeof location !== 'undefined' &&
+ /^(http|https):\/\/(localhost|127\.0\.0\.1)/.test(location.href);
+ function setDebug(value, filter) {
+ debug = value;
+ }
+
+ var INTERNAL = {};
+ var ZONE_ECHO_LIMIT = 100, _a$1 = typeof Promise === 'undefined' ?
+ [] :
+ (function () {
+ var globalP = Promise.resolve();
+ if (typeof crypto === 'undefined' || !crypto.subtle)
+ return [globalP, getProto(globalP), globalP];
+ var nativeP = crypto.subtle.digest("SHA-512", new Uint8Array([0]));
+ return [
+ nativeP,
+ getProto(nativeP),
+ globalP
+ ];
+ })(), resolvedNativePromise = _a$1[0], nativePromiseProto = _a$1[1], resolvedGlobalPromise = _a$1[2], nativePromiseThen = nativePromiseProto && nativePromiseProto.then;
+ var NativePromise = resolvedNativePromise && resolvedNativePromise.constructor;
+ var patchGlobalPromise = !!resolvedGlobalPromise;
+ function schedulePhysicalTick() {
+ queueMicrotask(physicalTick);
+ }
+ var asap = function (callback, args) {
+ microtickQueue.push([callback, args]);
+ if (needsNewPhysicalTick) {
+ schedulePhysicalTick();
+ needsNewPhysicalTick = false;
+ }
+ };
+ var isOutsideMicroTick = true,
+ needsNewPhysicalTick = true,
+ unhandledErrors = [],
+ rejectingErrors = [],
+ rejectionMapper = mirror;
+ var globalPSD = {
+ id: 'global',
+ global: true,
+ ref: 0,
+ unhandleds: [],
+ onunhandled: nop,
+ pgp: false,
+ env: {},
+ finalize: nop
+ };
+ var PSD = globalPSD;
+ var microtickQueue = [];
+ var numScheduledCalls = 0;
+ var tickFinalizers = [];
+ function DexiePromise(fn) {
+ if (typeof this !== 'object')
+ throw new TypeError('Promises must be constructed via new');
+ this._listeners = [];
+ this._lib = false;
+ var psd = (this._PSD = PSD);
+ if (typeof fn !== 'function') {
+ if (fn !== INTERNAL)
+ throw new TypeError('Not a function');
+ this._state = arguments[1];
+ this._value = arguments[2];
+ if (this._state === false)
+ handleRejection(this, this._value);
+ return;
+ }
+ this._state = null;
+ this._value = null;
+ ++psd.ref;
+ executePromiseTask(this, fn);
+ }
+ var thenProp = {
+ get: function () {
+ var psd = PSD, microTaskId = totalEchoes;
+ function then(onFulfilled, onRejected) {
+ var _this = this;
+ var possibleAwait = !psd.global && (psd !== PSD || microTaskId !== totalEchoes);
+ var cleanup = possibleAwait && !decrementExpectedAwaits();
+ var rv = new DexiePromise(function (resolve, reject) {
+ propagateToListener(_this, new Listener(nativeAwaitCompatibleWrap(onFulfilled, psd, possibleAwait, cleanup), nativeAwaitCompatibleWrap(onRejected, psd, possibleAwait, cleanup), resolve, reject, psd));
+ });
+ if (this._consoleTask)
+ rv._consoleTask = this._consoleTask;
+ return rv;
+ }
+ then.prototype = INTERNAL;
+ return then;
+ },
+ set: function (value) {
+ setProp(this, 'then', value && value.prototype === INTERNAL ?
+ thenProp :
+ {
+ get: function () {
+ return value;
+ },
+ set: thenProp.set
+ });
+ }
+ };
+ props(DexiePromise.prototype, {
+ then: thenProp,
+ _then: function (onFulfilled, onRejected) {
+ propagateToListener(this, new Listener(null, null, onFulfilled, onRejected, PSD));
+ },
+ catch: function (onRejected) {
+ if (arguments.length === 1)
+ return this.then(null, onRejected);
+ var type = arguments[0], handler = arguments[1];
+ return typeof type === 'function' ? this.then(null, function (err) {
+ return err instanceof type ? handler(err) : PromiseReject(err);
+ })
+ : this.then(null, function (err) {
+ return err && err.name === type ? handler(err) : PromiseReject(err);
+ });
+ },
+ finally: function (onFinally) {
+ return this.then(function (value) {
+ return DexiePromise.resolve(onFinally()).then(function () { return value; });
+ }, function (err) {
+ return DexiePromise.resolve(onFinally()).then(function () { return PromiseReject(err); });
+ });
+ },
+ timeout: function (ms, msg) {
+ var _this = this;
+ return ms < Infinity ?
+ new DexiePromise(function (resolve, reject) {
+ var handle = setTimeout(function () { return reject(new exceptions.Timeout(msg)); }, ms);
+ _this.then(resolve, reject).finally(clearTimeout.bind(null, handle));
+ }) : this;
+ }
+ });
+ if (typeof Symbol !== 'undefined' && Symbol.toStringTag)
+ setProp(DexiePromise.prototype, Symbol.toStringTag, 'Dexie.Promise');
+ globalPSD.env = snapShot();
+ function Listener(onFulfilled, onRejected, resolve, reject, zone) {
+ this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
+ this.onRejected = typeof onRejected === 'function' ? onRejected : null;
+ this.resolve = resolve;
+ this.reject = reject;
+ this.psd = zone;
+ }
+ props(DexiePromise, {
+ all: function () {
+ var values = getArrayOf.apply(null, arguments)
+ .map(onPossibleParallellAsync);
+ return new DexiePromise(function (resolve, reject) {
+ if (values.length === 0)
+ resolve([]);
+ var remaining = values.length;
+ values.forEach(function (a, i) { return DexiePromise.resolve(a).then(function (x) {
+ values[i] = x;
+ if (!--remaining)
+ resolve(values);
+ }, reject); });
+ });
+ },
+ resolve: function (value) {
+ if (value instanceof DexiePromise)
+ return value;
+ if (value && typeof value.then === 'function')
+ return new DexiePromise(function (resolve, reject) {
+ value.then(resolve, reject);
+ });
+ var rv = new DexiePromise(INTERNAL, true, value);
+ return rv;
+ },
+ reject: PromiseReject,
+ race: function () {
+ var values = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync);
+ return new DexiePromise(function (resolve, reject) {
+ values.map(function (value) { return DexiePromise.resolve(value).then(resolve, reject); });
+ });
+ },
+ PSD: {
+ get: function () { return PSD; },
+ set: function (value) { return PSD = value; }
+ },
+ totalEchoes: { get: function () { return totalEchoes; } },
+ newPSD: newScope,
+ usePSD: usePSD,
+ scheduler: {
+ get: function () { return asap; },
+ set: function (value) { asap = value; }
+ },
+ rejectionMapper: {
+ get: function () { return rejectionMapper; },
+ set: function (value) { rejectionMapper = value; }
+ },
+ follow: function (fn, zoneProps) {
+ return new DexiePromise(function (resolve, reject) {
+ return newScope(function (resolve, reject) {
+ var psd = PSD;
+ psd.unhandleds = [];
+ psd.onunhandled = reject;
+ psd.finalize = callBoth(function () {
+ var _this = this;
+ run_at_end_of_this_or_next_physical_tick(function () {
+ _this.unhandleds.length === 0 ? resolve() : reject(_this.unhandleds[0]);
+ });
+ }, psd.finalize);
+ fn();
+ }, zoneProps, resolve, reject);
+ });
+ }
+ });
+ if (NativePromise) {
+ if (NativePromise.allSettled)
+ setProp(DexiePromise, "allSettled", function () {
+ var possiblePromises = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync);
+ return new DexiePromise(function (resolve) {
+ if (possiblePromises.length === 0)
+ resolve([]);
+ var remaining = possiblePromises.length;
+ var results = new Array(remaining);
+ possiblePromises.forEach(function (p, i) { return DexiePromise.resolve(p).then(function (value) { return results[i] = { status: "fulfilled", value: value }; }, function (reason) { return results[i] = { status: "rejected", reason: reason }; })
+ .then(function () { return --remaining || resolve(results); }); });
+ });
+ });
+ if (NativePromise.any && typeof AggregateError !== 'undefined')
+ setProp(DexiePromise, "any", function () {
+ var possiblePromises = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync);
+ return new DexiePromise(function (resolve, reject) {
+ if (possiblePromises.length === 0)
+ reject(new AggregateError([]));
+ var remaining = possiblePromises.length;
+ var failures = new Array(remaining);
+ possiblePromises.forEach(function (p, i) { return DexiePromise.resolve(p).then(function (value) { return resolve(value); }, function (failure) {
+ failures[i] = failure;
+ if (!--remaining)
+ reject(new AggregateError(failures));
+ }); });
+ });
+ });
+ if (NativePromise.withResolvers)
+ DexiePromise.withResolvers = NativePromise.withResolvers;
+ }
+ function executePromiseTask(promise, fn) {
+ try {
+ fn(function (value) {
+ if (promise._state !== null)
+ return;
+ if (value === promise)
+ throw new TypeError('A promise cannot be resolved with itself.');
+ var shouldExecuteTick = promise._lib && beginMicroTickScope();
+ if (value && typeof value.then === 'function') {
+ executePromiseTask(promise, function (resolve, reject) {
+ value instanceof DexiePromise ?
+ value._then(resolve, reject) :
+ value.then(resolve, reject);
+ });
+ }
+ else {
+ promise._state = true;
+ promise._value = value;
+ propagateAllListeners(promise);
+ }
+ if (shouldExecuteTick)
+ endMicroTickScope();
+ }, handleRejection.bind(null, promise));
+ }
+ catch (ex) {
+ handleRejection(promise, ex);
+ }
+ }
+ function handleRejection(promise, reason) {
+ rejectingErrors.push(reason);
+ if (promise._state !== null)
+ return;
+ var shouldExecuteTick = promise._lib && beginMicroTickScope();
+ reason = rejectionMapper(reason);
+ promise._state = false;
+ promise._value = reason;
+ addPossiblyUnhandledError(promise);
+ propagateAllListeners(promise);
+ if (shouldExecuteTick)
+ endMicroTickScope();
+ }
+ function propagateAllListeners(promise) {
+ var listeners = promise._listeners;
+ promise._listeners = [];
+ for (var i = 0, len = listeners.length; i < len; ++i) {
+ propagateToListener(promise, listeners[i]);
+ }
+ var psd = promise._PSD;
+ --psd.ref || psd.finalize();
+ if (numScheduledCalls === 0) {
+ ++numScheduledCalls;
+ asap(function () {
+ if (--numScheduledCalls === 0)
+ finalizePhysicalTick();
+ }, []);
+ }
+ }
+ function propagateToListener(promise, listener) {
+ if (promise._state === null) {
+ promise._listeners.push(listener);
+ return;
+ }
+ var cb = promise._state ? listener.onFulfilled : listener.onRejected;
+ if (cb === null) {
+ return (promise._state ? listener.resolve : listener.reject)(promise._value);
+ }
+ ++listener.psd.ref;
+ ++numScheduledCalls;
+ asap(callListener, [cb, promise, listener]);
+ }
+ function callListener(cb, promise, listener) {
+ try {
+ var ret, value = promise._value;
+ if (!promise._state && rejectingErrors.length)
+ rejectingErrors = [];
+ ret = debug && promise._consoleTask ? promise._consoleTask.run(function () { return cb(value); }) : cb(value);
+ if (!promise._state && rejectingErrors.indexOf(value) === -1) {
+ markErrorAsHandled(promise);
+ }
+ listener.resolve(ret);
+ }
+ catch (e) {
+ listener.reject(e);
+ }
+ finally {
+ if (--numScheduledCalls === 0)
+ finalizePhysicalTick();
+ --listener.psd.ref || listener.psd.finalize();
+ }
+ }
+ function physicalTick() {
+ usePSD(globalPSD, function () {
+ beginMicroTickScope() && endMicroTickScope();
+ });
+ }
+ function beginMicroTickScope() {
+ var wasRootExec = isOutsideMicroTick;
+ isOutsideMicroTick = false;
+ needsNewPhysicalTick = false;
+ return wasRootExec;
+ }
+ function endMicroTickScope() {
+ var callbacks, i, l;
+ do {
+ while (microtickQueue.length > 0) {
+ callbacks = microtickQueue;
+ microtickQueue = [];
+ l = callbacks.length;
+ for (i = 0; i < l; ++i) {
+ var item = callbacks[i];
+ item[0].apply(null, item[1]);
+ }
+ }
+ } while (microtickQueue.length > 0);
+ isOutsideMicroTick = true;
+ needsNewPhysicalTick = true;
+ }
+ function finalizePhysicalTick() {
+ var unhandledErrs = unhandledErrors;
+ unhandledErrors = [];
+ unhandledErrs.forEach(function (p) {
+ p._PSD.onunhandled.call(null, p._value, p);
+ });
+ var finalizers = tickFinalizers.slice(0);
+ var i = finalizers.length;
+ while (i)
+ finalizers[--i]();
+ }
+ function run_at_end_of_this_or_next_physical_tick(fn) {
+ function finalizer() {
+ fn();
+ tickFinalizers.splice(tickFinalizers.indexOf(finalizer), 1);
+ }
+ tickFinalizers.push(finalizer);
+ ++numScheduledCalls;
+ asap(function () {
+ if (--numScheduledCalls === 0)
+ finalizePhysicalTick();
+ }, []);
+ }
+ function addPossiblyUnhandledError(promise) {
+ if (!unhandledErrors.some(function (p) { return p._value === promise._value; }))
+ unhandledErrors.push(promise);
+ }
+ function markErrorAsHandled(promise) {
+ var i = unhandledErrors.length;
+ while (i)
+ if (unhandledErrors[--i]._value === promise._value) {
+ unhandledErrors.splice(i, 1);
+ return;
+ }
+ }
+ function PromiseReject(reason) {
+ return new DexiePromise(INTERNAL, false, reason);
+ }
+ function wrap(fn, errorCatcher) {
+ var psd = PSD;
+ return function () {
+ var wasRootExec = beginMicroTickScope(), outerScope = PSD;
+ try {
+ switchToZone(psd, true);
+ return fn.apply(this, arguments);
+ }
+ catch (e) {
+ errorCatcher && errorCatcher(e);
+ }
+ finally {
+ switchToZone(outerScope, false);
+ if (wasRootExec)
+ endMicroTickScope();
+ }
+ };
+ }
+ var task = { awaits: 0, echoes: 0, id: 0 };
+ var taskCounter = 0;
+ var zoneStack = [];
+ var zoneEchoes = 0;
+ var totalEchoes = 0;
+ var zone_id_counter = 0;
+ function newScope(fn, props, a1, a2) {
+ var parent = PSD, psd = Object.create(parent);
+ psd.parent = parent;
+ psd.ref = 0;
+ psd.global = false;
+ psd.id = ++zone_id_counter;
+ globalPSD.env;
+ psd.env = patchGlobalPromise ? {
+ Promise: DexiePromise,
+ PromiseProp: { value: DexiePromise, configurable: true, writable: true },
+ all: DexiePromise.all,
+ race: DexiePromise.race,
+ allSettled: DexiePromise.allSettled,
+ any: DexiePromise.any,
+ resolve: DexiePromise.resolve,
+ reject: DexiePromise.reject,
+ } : {};
+ if (props)
+ extend(psd, props);
+ ++parent.ref;
+ psd.finalize = function () {
+ --this.parent.ref || this.parent.finalize();
+ };
+ var rv = usePSD(psd, fn, a1, a2);
+ if (psd.ref === 0)
+ psd.finalize();
+ return rv;
+ }
+ function incrementExpectedAwaits() {
+ if (!task.id)
+ task.id = ++taskCounter;
+ ++task.awaits;
+ task.echoes += ZONE_ECHO_LIMIT;
+ return task.id;
+ }
+ function decrementExpectedAwaits() {
+ if (!task.awaits)
+ return false;
+ if (--task.awaits === 0)
+ task.id = 0;
+ task.echoes = task.awaits * ZONE_ECHO_LIMIT;
+ return true;
+ }
+ if (('' + nativePromiseThen).indexOf('[native code]') === -1) {
+ incrementExpectedAwaits = decrementExpectedAwaits = nop;
+ }
+ function onPossibleParallellAsync(possiblePromise) {
+ if (task.echoes && possiblePromise && possiblePromise.constructor === NativePromise) {
+ incrementExpectedAwaits();
+ return possiblePromise.then(function (x) {
+ decrementExpectedAwaits();
+ return x;
+ }, function (e) {
+ decrementExpectedAwaits();
+ return rejection(e);
+ });
+ }
+ return possiblePromise;
+ }
+ function zoneEnterEcho(targetZone) {
+ ++totalEchoes;
+ if (!task.echoes || --task.echoes === 0) {
+ task.echoes = task.awaits = task.id = 0;
+ }
+ zoneStack.push(PSD);
+ switchToZone(targetZone, true);
+ }
+ function zoneLeaveEcho() {
+ var zone = zoneStack[zoneStack.length - 1];
+ zoneStack.pop();
+ switchToZone(zone, false);
+ }
+ function switchToZone(targetZone, bEnteringZone) {
+ var currentZone = PSD;
+ if (bEnteringZone ? task.echoes && (!zoneEchoes++ || targetZone !== PSD) : zoneEchoes && (!--zoneEchoes || targetZone !== PSD)) {
+ queueMicrotask(bEnteringZone ? zoneEnterEcho.bind(null, targetZone) : zoneLeaveEcho);
+ }
+ if (targetZone === PSD)
+ return;
+ PSD = targetZone;
+ if (currentZone === globalPSD)
+ globalPSD.env = snapShot();
+ if (patchGlobalPromise) {
+ var GlobalPromise = globalPSD.env.Promise;
+ var targetEnv = targetZone.env;
+ if (currentZone.global || targetZone.global) {
+ Object.defineProperty(_global, 'Promise', targetEnv.PromiseProp);
+ GlobalPromise.all = targetEnv.all;
+ GlobalPromise.race = targetEnv.race;
+ GlobalPromise.resolve = targetEnv.resolve;
+ GlobalPromise.reject = targetEnv.reject;
+ if (targetEnv.allSettled)
+ GlobalPromise.allSettled = targetEnv.allSettled;
+ if (targetEnv.any)
+ GlobalPromise.any = targetEnv.any;
+ }
+ }
+ }
+ function snapShot() {
+ var GlobalPromise = _global.Promise;
+ return patchGlobalPromise ? {
+ Promise: GlobalPromise,
+ PromiseProp: Object.getOwnPropertyDescriptor(_global, "Promise"),
+ all: GlobalPromise.all,
+ race: GlobalPromise.race,
+ allSettled: GlobalPromise.allSettled,
+ any: GlobalPromise.any,
+ resolve: GlobalPromise.resolve,
+ reject: GlobalPromise.reject,
+ } : {};
+ }
+ function usePSD(psd, fn, a1, a2, a3) {
+ var outerScope = PSD;
+ try {
+ switchToZone(psd, true);
+ return fn(a1, a2, a3);
+ }
+ finally {
+ switchToZone(outerScope, false);
+ }
+ }
+ function nativeAwaitCompatibleWrap(fn, zone, possibleAwait, cleanup) {
+ return typeof fn !== 'function' ? fn : function () {
+ var outerZone = PSD;
+ if (possibleAwait)
+ incrementExpectedAwaits();
+ switchToZone(zone, true);
+ try {
+ return fn.apply(this, arguments);
+ }
+ finally {
+ switchToZone(outerZone, false);
+ if (cleanup)
+ queueMicrotask(decrementExpectedAwaits);
+ }
+ };
+ }
+ function execInGlobalContext(cb) {
+ if (Promise === NativePromise && task.echoes === 0) {
+ if (zoneEchoes === 0) {
+ cb();
+ }
+ else {
+ enqueueNativeMicroTask(cb);
+ }
+ }
+ else {
+ setTimeout(cb, 0);
+ }
+ }
+ var rejection = DexiePromise.reject;
+
+ function tempTransaction(db, mode, storeNames, fn) {
+ if (!db.idbdb || (!db._state.openComplete && (!PSD.letThrough && !db._vip))) {
+ if (db._state.openComplete) {
+ return rejection(new exceptions.DatabaseClosed(db._state.dbOpenError));
+ }
+ if (!db._state.isBeingOpened) {
+ if (!db._state.autoOpen)
+ return rejection(new exceptions.DatabaseClosed());
+ db.open().catch(nop);
+ }
+ return db._state.dbReadyPromise.then(function () { return tempTransaction(db, mode, storeNames, fn); });
+ }
+ else {
+ var trans = db._createTransaction(mode, storeNames, db._dbSchema);
+ try {
+ trans.create();
+ db._state.PR1398_maxLoop = 3;
+ }
+ catch (ex) {
+ if (ex.name === errnames.InvalidState && db.isOpen() && --db._state.PR1398_maxLoop > 0) {
+ console.warn('Dexie: Need to reopen db');
+ db.close({ disableAutoOpen: false });
+ return db.open().then(function () { return tempTransaction(db, mode, storeNames, fn); });
+ }
+ return rejection(ex);
+ }
+ return trans._promise(mode, function (resolve, reject) {
+ return newScope(function () {
+ PSD.trans = trans;
+ return fn(resolve, reject, trans);
+ });
+ }).then(function (result) {
+ if (mode === 'readwrite')
+ try {
+ trans.idbtrans.commit();
+ }
+ catch (_a) { }
+ return mode === 'readonly' ? result : trans._completion.then(function () { return result; });
+ });
+ }
+ }
+
+ var DEXIE_VERSION = '4.2.1';
+ var maxString = String.fromCharCode(65535);
+ var minKey = -Infinity;
+ var INVALID_KEY_ARGUMENT = "Invalid key provided. Keys must be of type string, number, Date or Array.";
+ var STRING_EXPECTED = "String expected.";
+ var connections = [];
+ var DBNAMES_DB = '__dbnames';
+ var READONLY = 'readonly';
+ var READWRITE = 'readwrite';
+
+ function combine(filter1, filter2) {
+ return filter1 ?
+ filter2 ?
+ function () { return filter1.apply(this, arguments) && filter2.apply(this, arguments); } :
+ filter1 :
+ filter2;
+ }
+
+ var AnyRange = {
+ type: 3 ,
+ lower: -Infinity,
+ lowerOpen: false,
+ upper: [[]],
+ upperOpen: false
+ };
+
+ function workaroundForUndefinedPrimKey(keyPath) {
+ return typeof keyPath === "string" && !/\./.test(keyPath)
+ ? function (obj) {
+ if (obj[keyPath] === undefined && (keyPath in obj)) {
+ obj = deepClone(obj);
+ delete obj[keyPath];
+ }
+ return obj;
+ }
+ : function (obj) { return obj; };
+ }
+
+ function Entity() {
+ throw exceptions.Type("Entity instances must never be new:ed. Instances are generated by the framework bypassing the constructor.");
+ }
+
+ function cmp(a, b) {
+ try {
+ var ta = type(a);
+ var tb = type(b);
+ if (ta !== tb) {
+ if (ta === 'Array')
+ return 1;
+ if (tb === 'Array')
+ return -1;
+ if (ta === 'binary')
+ return 1;
+ if (tb === 'binary')
+ return -1;
+ if (ta === 'string')
+ return 1;
+ if (tb === 'string')
+ return -1;
+ if (ta === 'Date')
+ return 1;
+ if (tb !== 'Date')
+ return NaN;
+ return -1;
+ }
+ switch (ta) {
+ case 'number':
+ case 'Date':
+ case 'string':
+ return a > b ? 1 : a < b ? -1 : 0;
+ case 'binary': {
+ return compareUint8Arrays(getUint8Array(a), getUint8Array(b));
+ }
+ case 'Array':
+ return compareArrays(a, b);
+ }
+ }
+ catch (_a) { }
+ return NaN;
+ }
+ function compareArrays(a, b) {
+ var al = a.length;
+ var bl = b.length;
+ var l = al < bl ? al : bl;
+ for (var i = 0; i < l; ++i) {
+ var res = cmp(a[i], b[i]);
+ if (res !== 0)
+ return res;
+ }
+ return al === bl ? 0 : al < bl ? -1 : 1;
+ }
+ function compareUint8Arrays(a, b) {
+ var al = a.length;
+ var bl = b.length;
+ var l = al < bl ? al : bl;
+ for (var i = 0; i < l; ++i) {
+ if (a[i] !== b[i])
+ return a[i] < b[i] ? -1 : 1;
+ }
+ return al === bl ? 0 : al < bl ? -1 : 1;
+ }
+ function type(x) {
+ var t = typeof x;
+ if (t !== 'object')
+ return t;
+ if (ArrayBuffer.isView(x))
+ return 'binary';
+ var tsTag = toStringTag(x);
+ return tsTag === 'ArrayBuffer' ? 'binary' : tsTag;
+ }
+ function getUint8Array(a) {
+ if (a instanceof Uint8Array)
+ return a;
+ if (ArrayBuffer.isView(a))
+ return new Uint8Array(a.buffer, a.byteOffset, a.byteLength);
+ return new Uint8Array(a);
+ }
+
+ function builtInDeletionTrigger(table, keys, res) {
+ var yProps = table.schema.yProps;
+ if (!yProps)
+ return res;
+ if (keys && res.numFailures > 0)
+ keys = keys.filter(function (_, i) { return !res.failures[i]; });
+ return Promise.all(yProps.map(function (_a) {
+ var updatesTable = _a.updatesTable;
+ return keys
+ ? table.db.table(updatesTable).where('k').anyOf(keys).delete()
+ : table.db.table(updatesTable).clear();
+ })).then(function () { return res; });
+ }
+
+ var PropModification = (function () {
+ function PropModification(spec) {
+ this["@@propmod"] = spec;
+ }
+ PropModification.prototype.execute = function (value) {
+ var _a;
+ var spec = this["@@propmod"];
+ if (spec.add !== undefined) {
+ var term = spec.add;
+ if (isArray(term)) {
+ return __spreadArray(__spreadArray([], (isArray(value) ? value : []), true), term, true).sort();
+ }
+ if (typeof term === 'number')
+ return (Number(value) || 0) + term;
+ if (typeof term === 'bigint') {
+ try {
+ return BigInt(value) + term;
+ }
+ catch (_b) {
+ return BigInt(0) + term;
+ }
+ }
+ throw new TypeError("Invalid term ".concat(term));
+ }
+ if (spec.remove !== undefined) {
+ var subtrahend_1 = spec.remove;
+ if (isArray(subtrahend_1)) {
+ return isArray(value) ? value.filter(function (item) { return !subtrahend_1.includes(item); }).sort() : [];
+ }
+ if (typeof subtrahend_1 === 'number')
+ return Number(value) - subtrahend_1;
+ if (typeof subtrahend_1 === 'bigint') {
+ try {
+ return BigInt(value) - subtrahend_1;
+ }
+ catch (_c) {
+ return BigInt(0) - subtrahend_1;
+ }
+ }
+ throw new TypeError("Invalid subtrahend ".concat(subtrahend_1));
+ }
+ var prefixToReplace = (_a = spec.replacePrefix) === null || _a === void 0 ? void 0 : _a[0];
+ if (prefixToReplace && typeof value === 'string' && value.startsWith(prefixToReplace)) {
+ return spec.replacePrefix[1] + value.substring(prefixToReplace.length);
+ }
+ return value;
+ };
+ return PropModification;
+ }());
+
+ function applyUpdateSpec(obj, changes) {
+ var keyPaths = keys(changes);
+ var numKeys = keyPaths.length;
+ var anythingModified = false;
+ for (var i = 0; i < numKeys; ++i) {
+ var keyPath = keyPaths[i];
+ var value = changes[keyPath];
+ var origValue = getByKeyPath(obj, keyPath);
+ if (value instanceof PropModification) {
+ setByKeyPath(obj, keyPath, value.execute(origValue));
+ anythingModified = true;
+ }
+ else if (origValue !== value) {
+ setByKeyPath(obj, keyPath, value);
+ anythingModified = true;
+ }
+ }
+ return anythingModified;
+ }
+
+ var Table = (function () {
+ function Table() {
+ }
+ Table.prototype._trans = function (mode, fn, writeLocked) {
+ var trans = this._tx || PSD.trans;
+ var tableName = this.name;
+ var task = debug && typeof console !== 'undefined' && console.createTask && console.createTask("Dexie: ".concat(mode === 'readonly' ? 'read' : 'write', " ").concat(this.name));
+ function checkTableInTransaction(resolve, reject, trans) {
+ if (!trans.schema[tableName])
+ throw new exceptions.NotFound("Table " + tableName + " not part of transaction");
+ return fn(trans.idbtrans, trans);
+ }
+ var wasRootExec = beginMicroTickScope();
+ try {
+ var p = trans && trans.db._novip === this.db._novip ?
+ trans === PSD.trans ?
+ trans._promise(mode, checkTableInTransaction, writeLocked) :
+ newScope(function () { return trans._promise(mode, checkTableInTransaction, writeLocked); }, { trans: trans, transless: PSD.transless || PSD }) :
+ tempTransaction(this.db, mode, [this.name], checkTableInTransaction);
+ if (task) {
+ p._consoleTask = task;
+ p = p.catch(function (err) {
+ console.trace(err);
+ return rejection(err);
+ });
+ }
+ return p;
+ }
+ finally {
+ if (wasRootExec)
+ endMicroTickScope();
+ }
+ };
+ Table.prototype.get = function (keyOrCrit, cb) {
+ var _this = this;
+ if (keyOrCrit && keyOrCrit.constructor === Object)
+ return this.where(keyOrCrit).first(cb);
+ if (keyOrCrit == null)
+ return rejection(new exceptions.Type("Invalid argument to Table.get()"));
+ return this._trans('readonly', function (trans) {
+ return _this.core.get({ trans: trans, key: keyOrCrit })
+ .then(function (res) { return _this.hook.reading.fire(res); });
+ }).then(cb);
+ };
+ Table.prototype.where = function (indexOrCrit) {
+ if (typeof indexOrCrit === 'string')
+ return new this.db.WhereClause(this, indexOrCrit);
+ if (isArray(indexOrCrit))
+ return new this.db.WhereClause(this, "[".concat(indexOrCrit.join('+'), "]"));
+ var keyPaths = keys(indexOrCrit);
+ if (keyPaths.length === 1)
+ return this
+ .where(keyPaths[0])
+ .equals(indexOrCrit[keyPaths[0]]);
+ var compoundIndex = this.schema.indexes.concat(this.schema.primKey).filter(function (ix) {
+ if (ix.compound &&
+ keyPaths.every(function (keyPath) { return ix.keyPath.indexOf(keyPath) >= 0; })) {
+ for (var i = 0; i < keyPaths.length; ++i) {
+ if (keyPaths.indexOf(ix.keyPath[i]) === -1)
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }).sort(function (a, b) { return a.keyPath.length - b.keyPath.length; })[0];
+ if (compoundIndex && this.db._maxKey !== maxString) {
+ var keyPathsInValidOrder = compoundIndex.keyPath.slice(0, keyPaths.length);
+ return this
+ .where(keyPathsInValidOrder)
+ .equals(keyPathsInValidOrder.map(function (kp) { return indexOrCrit[kp]; }));
+ }
+ if (!compoundIndex && debug)
+ console.warn("The query ".concat(JSON.stringify(indexOrCrit), " on ").concat(this.name, " would benefit from a ") +
+ "compound index [".concat(keyPaths.join('+'), "]"));
+ var idxByName = this.schema.idxByName;
+ function equals(a, b) {
+ return cmp(a, b) === 0;
+ }
+ var _a = keyPaths.reduce(function (_a, keyPath) {
+ var prevIndex = _a[0], prevFilterFn = _a[1];
+ var index = idxByName[keyPath];
+ var value = indexOrCrit[keyPath];
+ return [
+ prevIndex || index,
+ prevIndex || !index ?
+ combine(prevFilterFn, index && index.multi ?
+ function (x) {
+ var prop = getByKeyPath(x, keyPath);
+ return isArray(prop) && prop.some(function (item) { return equals(value, item); });
+ } : function (x) { return equals(value, getByKeyPath(x, keyPath)); })
+ : prevFilterFn
+ ];
+ }, [null, null]), idx = _a[0], filterFunction = _a[1];
+ return idx ?
+ this.where(idx.name).equals(indexOrCrit[idx.keyPath])
+ .filter(filterFunction) :
+ compoundIndex ?
+ this.filter(filterFunction) :
+ this.where(keyPaths).equals('');
+ };
+ Table.prototype.filter = function (filterFunction) {
+ return this.toCollection().and(filterFunction);
+ };
+ Table.prototype.count = function (thenShortcut) {
+ return this.toCollection().count(thenShortcut);
+ };
+ Table.prototype.offset = function (offset) {
+ return this.toCollection().offset(offset);
+ };
+ Table.prototype.limit = function (numRows) {
+ return this.toCollection().limit(numRows);
+ };
+ Table.prototype.each = function (callback) {
+ return this.toCollection().each(callback);
+ };
+ Table.prototype.toArray = function (thenShortcut) {
+ return this.toCollection().toArray(thenShortcut);
+ };
+ Table.prototype.toCollection = function () {
+ return new this.db.Collection(new this.db.WhereClause(this));
+ };
+ Table.prototype.orderBy = function (index) {
+ return new this.db.Collection(new this.db.WhereClause(this, isArray(index) ?
+ "[".concat(index.join('+'), "]") :
+ index));
+ };
+ Table.prototype.reverse = function () {
+ return this.toCollection().reverse();
+ };
+ Table.prototype.mapToClass = function (constructor) {
+ var _a = this, db = _a.db, tableName = _a.name;
+ this.schema.mappedClass = constructor;
+ if (constructor.prototype instanceof Entity) {
+ constructor = (function (_super) {
+ __extends(class_1, _super);
+ function class_1() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ Object.defineProperty(class_1.prototype, "db", {
+ get: function () { return db; },
+ enumerable: false,
+ configurable: true
+ });
+ class_1.prototype.table = function () { return tableName; };
+ return class_1;
+ }(constructor));
+ }
+ var inheritedProps = new Set();
+ for (var proto = constructor.prototype; proto; proto = getProto(proto)) {
+ Object.getOwnPropertyNames(proto).forEach(function (propName) { return inheritedProps.add(propName); });
+ }
+ var readHook = function (obj) {
+ if (!obj)
+ return obj;
+ var res = Object.create(constructor.prototype);
+ for (var m in obj)
+ if (!inheritedProps.has(m))
+ try {
+ res[m] = obj[m];
+ }
+ catch (_) { }
+ return res;
+ };
+ if (this.schema.readHook) {
+ this.hook.reading.unsubscribe(this.schema.readHook);
+ }
+ this.schema.readHook = readHook;
+ this.hook("reading", readHook);
+ return constructor;
+ };
+ Table.prototype.defineClass = function () {
+ function Class(content) {
+ extend(this, content);
+ }
+ return this.mapToClass(Class);
+ };
+ Table.prototype.add = function (obj, key) {
+ var _this = this;
+ var _a = this.schema.primKey, auto = _a.auto, keyPath = _a.keyPath;
+ var objToAdd = obj;
+ if (keyPath && auto) {
+ objToAdd = workaroundForUndefinedPrimKey(keyPath)(obj);
+ }
+ return this._trans('readwrite', function (trans) {
+ return _this.core.mutate({ trans: trans, type: 'add', keys: key != null ? [key] : null, values: [objToAdd] });
+ }).then(function (res) { return res.numFailures ? DexiePromise.reject(res.failures[0]) : res.lastResult; })
+ .then(function (lastResult) {
+ if (keyPath) {
+ try {
+ setByKeyPath(obj, keyPath, lastResult);
+ }
+ catch (_) { }
+ }
+ return lastResult;
+ });
+ };
+ Table.prototype.upsert = function (key, modifications) {
+ var _this = this;
+ var keyPath = this.schema.primKey.keyPath;
+ return this._trans('readwrite', function (trans) {
+ return _this.core.get({ trans: trans, key: key }).then(function (existing) {
+ var obj = existing !== null && existing !== void 0 ? existing : {};
+ applyUpdateSpec(obj, modifications);
+ if (keyPath)
+ setByKeyPath(obj, keyPath, key);
+ return _this.core.mutate({
+ trans: trans,
+ type: 'put',
+ values: [obj],
+ keys: [key],
+ upsert: true,
+ updates: { keys: [key], changeSpecs: [modifications] }
+ }).then(function (res) { return res.numFailures ? DexiePromise.reject(res.failures[0]) : !!existing; });
+ });
+ });
+ };
+ Table.prototype.update = function (keyOrObject, modifications) {
+ if (typeof keyOrObject === 'object' && !isArray(keyOrObject)) {
+ var key = getByKeyPath(keyOrObject, this.schema.primKey.keyPath);
+ if (key === undefined)
+ return rejection(new exceptions.InvalidArgument("Given object does not contain its primary key"));
+ return this.where(":id").equals(key).modify(modifications);
+ }
+ else {
+ return this.where(":id").equals(keyOrObject).modify(modifications);
+ }
+ };
+ Table.prototype.put = function (obj, key) {
+ var _this = this;
+ var _a = this.schema.primKey, auto = _a.auto, keyPath = _a.keyPath;
+ var objToAdd = obj;
+ if (keyPath && auto) {
+ objToAdd = workaroundForUndefinedPrimKey(keyPath)(obj);
+ }
+ return this._trans('readwrite', function (trans) { return _this.core.mutate({ trans: trans, type: 'put', values: [objToAdd], keys: key != null ? [key] : null }); })
+ .then(function (res) { return res.numFailures ? DexiePromise.reject(res.failures[0]) : res.lastResult; })
+ .then(function (lastResult) {
+ if (keyPath) {
+ try {
+ setByKeyPath(obj, keyPath, lastResult);
+ }
+ catch (_) { }
+ }
+ return lastResult;
+ });
+ };
+ Table.prototype.delete = function (key) {
+ var _this = this;
+ return this._trans('readwrite', function (trans) { return _this.core.mutate({ trans: trans, type: 'delete', keys: [key] })
+ .then(function (res) { return builtInDeletionTrigger(_this, [key], res); })
+ .then(function (res) { return res.numFailures ? DexiePromise.reject(res.failures[0]) : undefined; }); });
+ };
+ Table.prototype.clear = function () {
+ var _this = this;
+ return this._trans('readwrite', function (trans) { return _this.core.mutate({ trans: trans, type: 'deleteRange', range: AnyRange })
+ .then(function (res) { return builtInDeletionTrigger(_this, null, res); }); })
+ .then(function (res) { return res.numFailures ? DexiePromise.reject(res.failures[0]) : undefined; });
+ };
+ Table.prototype.bulkGet = function (keys) {
+ var _this = this;
+ return this._trans('readonly', function (trans) {
+ return _this.core.getMany({
+ keys: keys,
+ trans: trans
+ }).then(function (result) { return result.map(function (res) { return _this.hook.reading.fire(res); }); });
+ });
+ };
+ Table.prototype.bulkAdd = function (objects, keysOrOptions, options) {
+ var _this = this;
+ var keys = Array.isArray(keysOrOptions) ? keysOrOptions : undefined;
+ options = options || (keys ? undefined : keysOrOptions);
+ var wantResults = options ? options.allKeys : undefined;
+ return this._trans('readwrite', function (trans) {
+ var _a = _this.schema.primKey, auto = _a.auto, keyPath = _a.keyPath;
+ if (keyPath && keys)
+ throw new exceptions.InvalidArgument("bulkAdd(): keys argument invalid on tables with inbound keys");
+ if (keys && keys.length !== objects.length)
+ throw new exceptions.InvalidArgument("Arguments objects and keys must have the same length");
+ var numObjects = objects.length;
+ var objectsToAdd = keyPath && auto ?
+ objects.map(workaroundForUndefinedPrimKey(keyPath)) :
+ objects;
+ return _this.core.mutate({ trans: trans, type: 'add', keys: keys, values: objectsToAdd, wantResults: wantResults })
+ .then(function (_a) {
+ var numFailures = _a.numFailures, results = _a.results, lastResult = _a.lastResult, failures = _a.failures;
+ var result = wantResults ? results : lastResult;
+ if (numFailures === 0)
+ return result;
+ throw new BulkError("".concat(_this.name, ".bulkAdd(): ").concat(numFailures, " of ").concat(numObjects, " operations failed"), failures);
+ });
+ });
+ };
+ Table.prototype.bulkPut = function (objects, keysOrOptions, options) {
+ var _this = this;
+ var keys = Array.isArray(keysOrOptions) ? keysOrOptions : undefined;
+ options = options || (keys ? undefined : keysOrOptions);
+ var wantResults = options ? options.allKeys : undefined;
+ return this._trans('readwrite', function (trans) {
+ var _a = _this.schema.primKey, auto = _a.auto, keyPath = _a.keyPath;
+ if (keyPath && keys)
+ throw new exceptions.InvalidArgument("bulkPut(): keys argument invalid on tables with inbound keys");
+ if (keys && keys.length !== objects.length)
+ throw new exceptions.InvalidArgument("Arguments objects and keys must have the same length");
+ var numObjects = objects.length;
+ var objectsToPut = keyPath && auto ?
+ objects.map(workaroundForUndefinedPrimKey(keyPath)) :
+ objects;
+ return _this.core.mutate({ trans: trans, type: 'put', keys: keys, values: objectsToPut, wantResults: wantResults })
+ .then(function (_a) {
+ var numFailures = _a.numFailures, results = _a.results, lastResult = _a.lastResult, failures = _a.failures;
+ var result = wantResults ? results : lastResult;
+ if (numFailures === 0)
+ return result;
+ throw new BulkError("".concat(_this.name, ".bulkPut(): ").concat(numFailures, " of ").concat(numObjects, " operations failed"), failures);
+ });
+ });
+ };
+ Table.prototype.bulkUpdate = function (keysAndChanges) {
+ var _this = this;
+ var coreTable = this.core;
+ var keys = keysAndChanges.map(function (entry) { return entry.key; });
+ var changeSpecs = keysAndChanges.map(function (entry) { return entry.changes; });
+ var offsetMap = [];
+ return this._trans('readwrite', function (trans) {
+ return coreTable.getMany({ trans: trans, keys: keys, cache: 'clone' }).then(function (objs) {
+ var resultKeys = [];
+ var resultObjs = [];
+ keysAndChanges.forEach(function (_a, idx) {
+ var key = _a.key, changes = _a.changes;
+ var obj = objs[idx];
+ if (obj) {
+ for (var _i = 0, _b = Object.keys(changes); _i < _b.length; _i++) {
+ var keyPath = _b[_i];
+ var value = changes[keyPath];
+ if (keyPath === _this.schema.primKey.keyPath) {
+ if (cmp(value, key) !== 0) {
+ throw new exceptions.Constraint("Cannot update primary key in bulkUpdate()");
+ }
+ }
+ else {
+ setByKeyPath(obj, keyPath, value);
+ }
+ }
+ offsetMap.push(idx);
+ resultKeys.push(key);
+ resultObjs.push(obj);
+ }
+ });
+ var numEntries = resultKeys.length;
+ return coreTable
+ .mutate({
+ trans: trans,
+ type: 'put',
+ keys: resultKeys,
+ values: resultObjs,
+ updates: {
+ keys: keys,
+ changeSpecs: changeSpecs
+ }
+ })
+ .then(function (_a) {
+ var numFailures = _a.numFailures, failures = _a.failures;
+ if (numFailures === 0)
+ return numEntries;
+ for (var _i = 0, _b = Object.keys(failures); _i < _b.length; _i++) {
+ var offset = _b[_i];
+ var mappedOffset = offsetMap[Number(offset)];
+ if (mappedOffset != null) {
+ var failure = failures[offset];
+ delete failures[offset];
+ failures[mappedOffset] = failure;
+ }
+ }
+ throw new BulkError("".concat(_this.name, ".bulkUpdate(): ").concat(numFailures, " of ").concat(numEntries, " operations failed"), failures);
+ });
+ });
+ });
+ };
+ Table.prototype.bulkDelete = function (keys) {
+ var _this = this;
+ var numKeys = keys.length;
+ return this._trans('readwrite', function (trans) {
+ return _this.core.mutate({ trans: trans, type: 'delete', keys: keys })
+ .then(function (res) { return builtInDeletionTrigger(_this, keys, res); });
+ }).then(function (_a) {
+ var numFailures = _a.numFailures, lastResult = _a.lastResult, failures = _a.failures;
+ if (numFailures === 0)
+ return lastResult;
+ throw new BulkError("".concat(_this.name, ".bulkDelete(): ").concat(numFailures, " of ").concat(numKeys, " operations failed"), failures);
+ });
+ };
+ return Table;
+ }());
+
+ function Events(ctx) {
+ var evs = {};
+ var rv = function (eventName, subscriber) {
+ if (subscriber) {
+ var i = arguments.length, args = new Array(i - 1);
+ while (--i)
+ args[i - 1] = arguments[i];
+ evs[eventName].subscribe.apply(null, args);
+ return ctx;
+ }
+ else if (typeof (eventName) === 'string') {
+ return evs[eventName];
+ }
+ };
+ rv.addEventType = add;
+ for (var i = 1, l = arguments.length; i < l; ++i) {
+ add(arguments[i]);
+ }
+ return rv;
+ function add(eventName, chainFunction, defaultFunction) {
+ if (typeof eventName === 'object')
+ return addConfiguredEvents(eventName);
+ if (!chainFunction)
+ chainFunction = reverseStoppableEventChain;
+ if (!defaultFunction)
+ defaultFunction = nop;
+ var context = {
+ subscribers: [],
+ fire: defaultFunction,
+ subscribe: function (cb) {
+ if (context.subscribers.indexOf(cb) === -1) {
+ context.subscribers.push(cb);
+ context.fire = chainFunction(context.fire, cb);
+ }
+ },
+ unsubscribe: function (cb) {
+ context.subscribers = context.subscribers.filter(function (fn) { return fn !== cb; });
+ context.fire = context.subscribers.reduce(chainFunction, defaultFunction);
+ }
+ };
+ evs[eventName] = rv[eventName] = context;
+ return context;
+ }
+ function addConfiguredEvents(cfg) {
+ keys(cfg).forEach(function (eventName) {
+ var args = cfg[eventName];
+ if (isArray(args)) {
+ add(eventName, cfg[eventName][0], cfg[eventName][1]);
+ }
+ else if (args === 'asap') {
+ var context = add(eventName, mirror, function fire() {
+ var i = arguments.length, args = new Array(i);
+ while (i--)
+ args[i] = arguments[i];
+ context.subscribers.forEach(function (fn) {
+ asap$1(function fireEvent() {
+ fn.apply(null, args);
+ });
+ });
+ });
+ }
+ else
+ throw new exceptions.InvalidArgument("Invalid event config");
+ });
+ }
+ }
+
+ function makeClassConstructor(prototype, constructor) {
+ derive(constructor).from({ prototype: prototype });
+ return constructor;
+ }
+
+ function createTableConstructor(db) {
+ return makeClassConstructor(Table.prototype, function Table(name, tableSchema, trans) {
+ this.db = db;
+ this._tx = trans;
+ this.name = name;
+ this.schema = tableSchema;
+ this.hook = db._allTables[name] ? db._allTables[name].hook : Events(null, {
+ "creating": [hookCreatingChain, nop],
+ "reading": [pureFunctionChain, mirror],
+ "updating": [hookUpdatingChain, nop],
+ "deleting": [hookDeletingChain, nop]
+ });
+ });
+ }
+
+ function isPlainKeyRange(ctx, ignoreLimitFilter) {
+ return !(ctx.filter || ctx.algorithm || ctx.or) &&
+ (ignoreLimitFilter ? ctx.justLimit : !ctx.replayFilter);
+ }
+ function addFilter(ctx, fn) {
+ ctx.filter = combine(ctx.filter, fn);
+ }
+ function addReplayFilter(ctx, factory, isLimitFilter) {
+ var curr = ctx.replayFilter;
+ ctx.replayFilter = curr ? function () { return combine(curr(), factory()); } : factory;
+ ctx.justLimit = isLimitFilter && !curr;
+ }
+ function addMatchFilter(ctx, fn) {
+ ctx.isMatch = combine(ctx.isMatch, fn);
+ }
+ function getIndexOrStore(ctx, coreSchema) {
+ if (ctx.isPrimKey)
+ return coreSchema.primaryKey;
+ var index = coreSchema.getIndexByKeyPath(ctx.index);
+ if (!index)
+ throw new exceptions.Schema("KeyPath " + ctx.index + " on object store " + coreSchema.name + " is not indexed");
+ return index;
+ }
+ function openCursor(ctx, coreTable, trans) {
+ var index = getIndexOrStore(ctx, coreTable.schema);
+ return coreTable.openCursor({
+ trans: trans,
+ values: !ctx.keysOnly,
+ reverse: ctx.dir === 'prev',
+ unique: !!ctx.unique,
+ query: {
+ index: index,
+ range: ctx.range
+ }
+ });
+ }
+ function iter(ctx, fn, coreTrans, coreTable) {
+ var filter = ctx.replayFilter ? combine(ctx.filter, ctx.replayFilter()) : ctx.filter;
+ if (!ctx.or) {
+ return iterate(openCursor(ctx, coreTable, coreTrans), combine(ctx.algorithm, filter), fn, !ctx.keysOnly && ctx.valueMapper);
+ }
+ else {
+ var set_1 = {};
+ var union = function (item, cursor, advance) {
+ if (!filter || filter(cursor, advance, function (result) { return cursor.stop(result); }, function (err) { return cursor.fail(err); })) {
+ var primaryKey = cursor.primaryKey;
+ var key = '' + primaryKey;
+ if (key === '[object ArrayBuffer]')
+ key = '' + new Uint8Array(primaryKey);
+ if (!hasOwn(set_1, key)) {
+ set_1[key] = true;
+ fn(item, cursor, advance);
+ }
+ }
+ };
+ return Promise.all([
+ ctx.or._iterate(union, coreTrans),
+ iterate(openCursor(ctx, coreTable, coreTrans), ctx.algorithm, union, !ctx.keysOnly && ctx.valueMapper)
+ ]);
+ }
+ }
+ function iterate(cursorPromise, filter, fn, valueMapper) {
+ var mappedFn = valueMapper ? function (x, c, a) { return fn(valueMapper(x), c, a); } : fn;
+ var wrappedFn = wrap(mappedFn);
+ return cursorPromise.then(function (cursor) {
+ if (cursor) {
+ return cursor.start(function () {
+ var c = function () { return cursor.continue(); };
+ if (!filter || filter(cursor, function (advancer) { return c = advancer; }, function (val) { cursor.stop(val); c = nop; }, function (e) { cursor.fail(e); c = nop; }))
+ wrappedFn(cursor.value, cursor, function (advancer) { return c = advancer; });
+ c();
+ });
+ }
+ });
+ }
+
+ var Collection = (function () {
+ function Collection() {
+ }
+ Collection.prototype._read = function (fn, cb) {
+ var ctx = this._ctx;
+ return ctx.error ?
+ ctx.table._trans(null, rejection.bind(null, ctx.error)) :
+ ctx.table._trans('readonly', fn).then(cb);
+ };
+ Collection.prototype._write = function (fn) {
+ var ctx = this._ctx;
+ return ctx.error ?
+ ctx.table._trans(null, rejection.bind(null, ctx.error)) :
+ ctx.table._trans('readwrite', fn, "locked");
+ };
+ Collection.prototype._addAlgorithm = function (fn) {
+ var ctx = this._ctx;
+ ctx.algorithm = combine(ctx.algorithm, fn);
+ };
+ Collection.prototype._iterate = function (fn, coreTrans) {
+ return iter(this._ctx, fn, coreTrans, this._ctx.table.core);
+ };
+ Collection.prototype.clone = function (props) {
+ var rv = Object.create(this.constructor.prototype), ctx = Object.create(this._ctx);
+ if (props)
+ extend(ctx, props);
+ rv._ctx = ctx;
+ return rv;
+ };
+ Collection.prototype.raw = function () {
+ this._ctx.valueMapper = null;
+ return this;
+ };
+ Collection.prototype.each = function (fn) {
+ var ctx = this._ctx;
+ return this._read(function (trans) { return iter(ctx, fn, trans, ctx.table.core); });
+ };
+ Collection.prototype.count = function (cb) {
+ var _this = this;
+ return this._read(function (trans) {
+ var ctx = _this._ctx;
+ var coreTable = ctx.table.core;
+ if (isPlainKeyRange(ctx, true)) {
+ return coreTable.count({
+ trans: trans,
+ query: {
+ index: getIndexOrStore(ctx, coreTable.schema),
+ range: ctx.range
+ }
+ }).then(function (count) { return Math.min(count, ctx.limit); });
+ }
+ else {
+ var count = 0;
+ return iter(ctx, function () { ++count; return false; }, trans, coreTable)
+ .then(function () { return count; });
+ }
+ }).then(cb);
+ };
+ Collection.prototype.sortBy = function (keyPath, cb) {
+ var parts = keyPath.split('.').reverse(), lastPart = parts[0], lastIndex = parts.length - 1;
+ function getval(obj, i) {
+ if (i)
+ return getval(obj[parts[i]], i - 1);
+ return obj[lastPart];
+ }
+ var order = this._ctx.dir === "next" ? 1 : -1;
+ function sorter(a, b) {
+ var aVal = getval(a, lastIndex), bVal = getval(b, lastIndex);
+ return cmp(aVal, bVal) * order;
+ }
+ return this.toArray(function (a) {
+ return a.sort(sorter);
+ }).then(cb);
+ };
+ Collection.prototype.toArray = function (cb) {
+ var _this = this;
+ return this._read(function (trans) {
+ var ctx = _this._ctx;
+ if (ctx.dir === 'next' && isPlainKeyRange(ctx, true) && ctx.limit > 0) {
+ var valueMapper_1 = ctx.valueMapper;
+ var index = getIndexOrStore(ctx, ctx.table.core.schema);
+ return ctx.table.core.query({
+ trans: trans,
+ limit: ctx.limit,
+ values: true,
+ query: {
+ index: index,
+ range: ctx.range
+ }
+ }).then(function (_a) {
+ var result = _a.result;
+ return valueMapper_1 ? result.map(valueMapper_1) : result;
+ });
+ }
+ else {
+ var a_1 = [];
+ return iter(ctx, function (item) { return a_1.push(item); }, trans, ctx.table.core).then(function () { return a_1; });
+ }
+ }, cb);
+ };
+ Collection.prototype.offset = function (offset) {
+ var ctx = this._ctx;
+ if (offset <= 0)
+ return this;
+ ctx.offset += offset;
+ if (isPlainKeyRange(ctx)) {
+ addReplayFilter(ctx, function () {
+ var offsetLeft = offset;
+ return function (cursor, advance) {
+ if (offsetLeft === 0)
+ return true;
+ if (offsetLeft === 1) {
+ --offsetLeft;
+ return false;
+ }
+ advance(function () {
+ cursor.advance(offsetLeft);
+ offsetLeft = 0;
+ });
+ return false;
+ };
+ });
+ }
+ else {
+ addReplayFilter(ctx, function () {
+ var offsetLeft = offset;
+ return function () { return (--offsetLeft < 0); };
+ });
+ }
+ return this;
+ };
+ Collection.prototype.limit = function (numRows) {
+ this._ctx.limit = Math.min(this._ctx.limit, numRows);
+ addReplayFilter(this._ctx, function () {
+ var rowsLeft = numRows;
+ return function (cursor, advance, resolve) {
+ if (--rowsLeft <= 0)
+ advance(resolve);
+ return rowsLeft >= 0;
+ };
+ }, true);
+ return this;
+ };
+ Collection.prototype.until = function (filterFunction, bIncludeStopEntry) {
+ addFilter(this._ctx, function (cursor, advance, resolve) {
+ if (filterFunction(cursor.value)) {
+ advance(resolve);
+ return bIncludeStopEntry;
+ }
+ else {
+ return true;
+ }
+ });
+ return this;
+ };
+ Collection.prototype.first = function (cb) {
+ return this.limit(1).toArray(function (a) { return a[0]; }).then(cb);
+ };
+ Collection.prototype.last = function (cb) {
+ return this.reverse().first(cb);
+ };
+ Collection.prototype.filter = function (filterFunction) {
+ addFilter(this._ctx, function (cursor) {
+ return filterFunction(cursor.value);
+ });
+ addMatchFilter(this._ctx, filterFunction);
+ return this;
+ };
+ Collection.prototype.and = function (filter) {
+ return this.filter(filter);
+ };
+ Collection.prototype.or = function (indexName) {
+ return new this.db.WhereClause(this._ctx.table, indexName, this);
+ };
+ Collection.prototype.reverse = function () {
+ this._ctx.dir = (this._ctx.dir === "prev" ? "next" : "prev");
+ if (this._ondirectionchange)
+ this._ondirectionchange(this._ctx.dir);
+ return this;
+ };
+ Collection.prototype.desc = function () {
+ return this.reverse();
+ };
+ Collection.prototype.eachKey = function (cb) {
+ var ctx = this._ctx;
+ ctx.keysOnly = !ctx.isMatch;
+ return this.each(function (val, cursor) { cb(cursor.key, cursor); });
+ };
+ Collection.prototype.eachUniqueKey = function (cb) {
+ this._ctx.unique = "unique";
+ return this.eachKey(cb);
+ };
+ Collection.prototype.eachPrimaryKey = function (cb) {
+ var ctx = this._ctx;
+ ctx.keysOnly = !ctx.isMatch;
+ return this.each(function (val, cursor) { cb(cursor.primaryKey, cursor); });
+ };
+ Collection.prototype.keys = function (cb) {
+ var ctx = this._ctx;
+ ctx.keysOnly = !ctx.isMatch;
+ var a = [];
+ return this.each(function (item, cursor) {
+ a.push(cursor.key);
+ }).then(function () {
+ return a;
+ }).then(cb);
+ };
+ Collection.prototype.primaryKeys = function (cb) {
+ var ctx = this._ctx;
+ if (ctx.dir === 'next' && isPlainKeyRange(ctx, true) && ctx.limit > 0) {
+ return this._read(function (trans) {
+ var index = getIndexOrStore(ctx, ctx.table.core.schema);
+ return ctx.table.core.query({
+ trans: trans,
+ values: false,
+ limit: ctx.limit,
+ query: {
+ index: index,
+ range: ctx.range
+ }
+ });
+ }).then(function (_a) {
+ var result = _a.result;
+ return result;
+ }).then(cb);
+ }
+ ctx.keysOnly = !ctx.isMatch;
+ var a = [];
+ return this.each(function (item, cursor) {
+ a.push(cursor.primaryKey);
+ }).then(function () {
+ return a;
+ }).then(cb);
+ };
+ Collection.prototype.uniqueKeys = function (cb) {
+ this._ctx.unique = "unique";
+ return this.keys(cb);
+ };
+ Collection.prototype.firstKey = function (cb) {
+ return this.limit(1).keys(function (a) { return a[0]; }).then(cb);
+ };
+ Collection.prototype.lastKey = function (cb) {
+ return this.reverse().firstKey(cb);
+ };
+ Collection.prototype.distinct = function () {
+ var ctx = this._ctx, idx = ctx.index && ctx.table.schema.idxByName[ctx.index];
+ if (!idx || !idx.multi)
+ return this;
+ var set = {};
+ addFilter(this._ctx, function (cursor) {
+ var strKey = cursor.primaryKey.toString();
+ var found = hasOwn(set, strKey);
+ set[strKey] = true;
+ return !found;
+ });
+ return this;
+ };
+ Collection.prototype.modify = function (changes) {
+ var _this = this;
+ var ctx = this._ctx;
+ return this._write(function (trans) {
+ var modifyer;
+ if (typeof changes === 'function') {
+ modifyer = changes;
+ }
+ else {
+ modifyer = function (item) { return applyUpdateSpec(item, changes); };
+ }
+ var coreTable = ctx.table.core;
+ var _a = coreTable.schema.primaryKey, outbound = _a.outbound, extractKey = _a.extractKey;
+ var limit = 200;
+ var modifyChunkSize = _this.db._options.modifyChunkSize;
+ if (modifyChunkSize) {
+ if (typeof modifyChunkSize == 'object') {
+ limit = modifyChunkSize[coreTable.name] || modifyChunkSize['*'] || 200;
+ }
+ else {
+ limit = modifyChunkSize;
+ }
+ }
+ var totalFailures = [];
+ var successCount = 0;
+ var failedKeys = [];
+ var applyMutateResult = function (expectedCount, res) {
+ var failures = res.failures, numFailures = res.numFailures;
+ successCount += expectedCount - numFailures;
+ for (var _i = 0, _a = keys(failures); _i < _a.length; _i++) {
+ var pos = _a[_i];
+ totalFailures.push(failures[pos]);
+ }
+ };
+ var isUnconditionalDelete = changes === deleteCallback;
+ return _this.clone().primaryKeys().then(function (keys) {
+ var criteria = isPlainKeyRange(ctx) &&
+ ctx.limit === Infinity &&
+ (typeof changes !== 'function' || isUnconditionalDelete) && {
+ index: ctx.index,
+ range: ctx.range
+ };
+ var nextChunk = function (offset) {
+ var count = Math.min(limit, keys.length - offset);
+ var keysInChunk = keys.slice(offset, offset + count);
+ return (isUnconditionalDelete ? Promise.resolve([]) : coreTable.getMany({
+ trans: trans,
+ keys: keysInChunk,
+ cache: "immutable"
+ })).then(function (values) {
+ var addValues = [];
+ var putValues = [];
+ var putKeys = outbound ? [] : null;
+ var deleteKeys = isUnconditionalDelete ? keysInChunk : [];
+ if (!isUnconditionalDelete)
+ for (var i = 0; i < count; ++i) {
+ var origValue = values[i];
+ var ctx_1 = {
+ value: deepClone(origValue),
+ primKey: keys[offset + i]
+ };
+ if (modifyer.call(ctx_1, ctx_1.value, ctx_1) !== false) {
+ if (ctx_1.value == null) {
+ deleteKeys.push(keys[offset + i]);
+ }
+ else if (!outbound && cmp(extractKey(origValue), extractKey(ctx_1.value)) !== 0) {
+ deleteKeys.push(keys[offset + i]);
+ addValues.push(ctx_1.value);
+ }
+ else {
+ putValues.push(ctx_1.value);
+ if (outbound)
+ putKeys.push(keys[offset + i]);
+ }
+ }
+ }
+ return Promise.resolve(addValues.length > 0 &&
+ coreTable.mutate({ trans: trans, type: 'add', values: addValues })
+ .then(function (res) {
+ for (var pos in res.failures) {
+ deleteKeys.splice(parseInt(pos), 1);
+ }
+ applyMutateResult(addValues.length, res);
+ })).then(function () { return (putValues.length > 0 || (criteria && typeof changes === 'object')) &&
+ coreTable.mutate({
+ trans: trans,
+ type: 'put',
+ keys: putKeys,
+ values: putValues,
+ criteria: criteria,
+ changeSpec: typeof changes !== 'function'
+ && changes,
+ isAdditionalChunk: offset > 0
+ }).then(function (res) { return applyMutateResult(putValues.length, res); }); }).then(function () { return (deleteKeys.length > 0 || (criteria && isUnconditionalDelete)) &&
+ coreTable.mutate({
+ trans: trans,
+ type: 'delete',
+ keys: deleteKeys,
+ criteria: criteria,
+ isAdditionalChunk: offset > 0
+ }).then(function (res) { return builtInDeletionTrigger(ctx.table, deleteKeys, res); })
+ .then(function (res) { return applyMutateResult(deleteKeys.length, res); }); }).then(function () {
+ return keys.length > offset + count && nextChunk(offset + limit);
+ });
+ });
+ };
+ return nextChunk(0).then(function () {
+ if (totalFailures.length > 0)
+ throw new ModifyError("Error modifying one or more objects", totalFailures, successCount, failedKeys);
+ return keys.length;
+ });
+ });
+ });
+ };
+ Collection.prototype.delete = function () {
+ var ctx = this._ctx, range = ctx.range;
+ if (isPlainKeyRange(ctx) &&
+ !ctx.table.schema.yProps &&
+ (ctx.isPrimKey || range.type === 3 ))
+ {
+ return this._write(function (trans) {
+ var primaryKey = ctx.table.core.schema.primaryKey;
+ var coreRange = range;
+ return ctx.table.core.count({ trans: trans, query: { index: primaryKey, range: coreRange } }).then(function (count) {
+ return ctx.table.core.mutate({ trans: trans, type: 'deleteRange', range: coreRange })
+ .then(function (_a) {
+ var failures = _a.failures, numFailures = _a.numFailures;
+ if (numFailures)
+ throw new ModifyError("Could not delete some values", Object.keys(failures).map(function (pos) { return failures[pos]; }), count - numFailures);
+ return count - numFailures;
+ });
+ });
+ });
+ }
+ return this.modify(deleteCallback);
+ };
+ return Collection;
+ }());
+ var deleteCallback = function (value, ctx) { return ctx.value = null; };
+
+ function createCollectionConstructor(db) {
+ return makeClassConstructor(Collection.prototype, function Collection(whereClause, keyRangeGenerator) {
+ this.db = db;
+ var keyRange = AnyRange, error = null;
+ if (keyRangeGenerator)
+ try {
+ keyRange = keyRangeGenerator();
+ }
+ catch (ex) {
+ error = ex;
+ }
+ var whereCtx = whereClause._ctx;
+ var table = whereCtx.table;
+ var readingHook = table.hook.reading.fire;
+ this._ctx = {
+ table: table,
+ index: whereCtx.index,
+ isPrimKey: (!whereCtx.index || (table.schema.primKey.keyPath && whereCtx.index === table.schema.primKey.name)),
+ range: keyRange,
+ keysOnly: false,
+ dir: "next",
+ unique: "",
+ algorithm: null,
+ filter: null,
+ replayFilter: null,
+ justLimit: true,
+ isMatch: null,
+ offset: 0,
+ limit: Infinity,
+ error: error,
+ or: whereCtx.or,
+ valueMapper: readingHook !== mirror ? readingHook : null
+ };
+ });
+ }
+
+ function simpleCompare(a, b) {
+ return a < b ? -1 : a === b ? 0 : 1;
+ }
+ function simpleCompareReverse(a, b) {
+ return a > b ? -1 : a === b ? 0 : 1;
+ }
+
+ function fail(collectionOrWhereClause, err, T) {
+ var collection = collectionOrWhereClause instanceof WhereClause ?
+ new collectionOrWhereClause.Collection(collectionOrWhereClause) :
+ collectionOrWhereClause;
+ collection._ctx.error = T ? new T(err) : new TypeError(err);
+ return collection;
+ }
+ function emptyCollection(whereClause) {
+ return new whereClause.Collection(whereClause, function () { return rangeEqual(""); }).limit(0);
+ }
+ function upperFactory(dir) {
+ return dir === "next" ?
+ function (s) { return s.toUpperCase(); } :
+ function (s) { return s.toLowerCase(); };
+ }
+ function lowerFactory(dir) {
+ return dir === "next" ?
+ function (s) { return s.toLowerCase(); } :
+ function (s) { return s.toUpperCase(); };
+ }
+ function nextCasing(key, lowerKey, upperNeedle, lowerNeedle, cmp, dir) {
+ var length = Math.min(key.length, lowerNeedle.length);
+ var llp = -1;
+ for (var i = 0; i < length; ++i) {
+ var lwrKeyChar = lowerKey[i];
+ if (lwrKeyChar !== lowerNeedle[i]) {
+ if (cmp(key[i], upperNeedle[i]) < 0)
+ return key.substr(0, i) + upperNeedle[i] + upperNeedle.substr(i + 1);
+ if (cmp(key[i], lowerNeedle[i]) < 0)
+ return key.substr(0, i) + lowerNeedle[i] + upperNeedle.substr(i + 1);
+ if (llp >= 0)
+ return key.substr(0, llp) + lowerKey[llp] + upperNeedle.substr(llp + 1);
+ return null;
+ }
+ if (cmp(key[i], lwrKeyChar) < 0)
+ llp = i;
+ }
+ if (length < lowerNeedle.length && dir === "next")
+ return key + upperNeedle.substr(key.length);
+ if (length < key.length && dir === "prev")
+ return key.substr(0, upperNeedle.length);
+ return (llp < 0 ? null : key.substr(0, llp) + lowerNeedle[llp] + upperNeedle.substr(llp + 1));
+ }
+ function addIgnoreCaseAlgorithm(whereClause, match, needles, suffix) {
+ var upper, lower, compare, upperNeedles, lowerNeedles, direction, nextKeySuffix, needlesLen = needles.length;
+ if (!needles.every(function (s) { return typeof s === 'string'; })) {
+ return fail(whereClause, STRING_EXPECTED);
+ }
+ function initDirection(dir) {
+ upper = upperFactory(dir);
+ lower = lowerFactory(dir);
+ compare = (dir === "next" ? simpleCompare : simpleCompareReverse);
+ var needleBounds = needles.map(function (needle) {
+ return { lower: lower(needle), upper: upper(needle) };
+ }).sort(function (a, b) {
+ return compare(a.lower, b.lower);
+ });
+ upperNeedles = needleBounds.map(function (nb) { return nb.upper; });
+ lowerNeedles = needleBounds.map(function (nb) { return nb.lower; });
+ direction = dir;
+ nextKeySuffix = (dir === "next" ? "" : suffix);
+ }
+ initDirection("next");
+ var c = new whereClause.Collection(whereClause, function () { return createRange(upperNeedles[0], lowerNeedles[needlesLen - 1] + suffix); });
+ c._ondirectionchange = function (direction) {
+ initDirection(direction);
+ };
+ var firstPossibleNeedle = 0;
+ c._addAlgorithm(function (cursor, advance, resolve) {
+ var key = cursor.key;
+ if (typeof key !== 'string')
+ return false;
+ var lowerKey = lower(key);
+ if (match(lowerKey, lowerNeedles, firstPossibleNeedle)) {
+ return true;
+ }
+ else {
+ var lowestPossibleCasing = null;
+ for (var i = firstPossibleNeedle; i < needlesLen; ++i) {
+ var casing = nextCasing(key, lowerKey, upperNeedles[i], lowerNeedles[i], compare, direction);
+ if (casing === null && lowestPossibleCasing === null)
+ firstPossibleNeedle = i + 1;
+ else if (lowestPossibleCasing === null || compare(lowestPossibleCasing, casing) > 0) {
+ lowestPossibleCasing = casing;
+ }
+ }
+ if (lowestPossibleCasing !== null) {
+ advance(function () { cursor.continue(lowestPossibleCasing + nextKeySuffix); });
+ }
+ else {
+ advance(resolve);
+ }
+ return false;
+ }
+ });
+ return c;
+ }
+ function createRange(lower, upper, lowerOpen, upperOpen) {
+ return {
+ type: 2 ,
+ lower: lower,
+ upper: upper,
+ lowerOpen: lowerOpen,
+ upperOpen: upperOpen
+ };
+ }
+ function rangeEqual(value) {
+ return {
+ type: 1 ,
+ lower: value,
+ upper: value
+ };
+ }
+
+ var WhereClause = (function () {
+ function WhereClause() {
+ }
+ Object.defineProperty(WhereClause.prototype, "Collection", {
+ get: function () {
+ return this._ctx.table.db.Collection;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ WhereClause.prototype.between = function (lower, upper, includeLower, includeUpper) {
+ includeLower = includeLower !== false;
+ includeUpper = includeUpper === true;
+ try {
+ if ((this._cmp(lower, upper) > 0) ||
+ (this._cmp(lower, upper) === 0 && (includeLower || includeUpper) && !(includeLower && includeUpper)))
+ return emptyCollection(this);
+ return new this.Collection(this, function () { return createRange(lower, upper, !includeLower, !includeUpper); });
+ }
+ catch (e) {
+ return fail(this, INVALID_KEY_ARGUMENT);
+ }
+ };
+ WhereClause.prototype.equals = function (value) {
+ if (value == null)
+ return fail(this, INVALID_KEY_ARGUMENT);
+ return new this.Collection(this, function () { return rangeEqual(value); });
+ };
+ WhereClause.prototype.above = function (value) {
+ if (value == null)
+ return fail(this, INVALID_KEY_ARGUMENT);
+ return new this.Collection(this, function () { return createRange(value, undefined, true); });
+ };
+ WhereClause.prototype.aboveOrEqual = function (value) {
+ if (value == null)
+ return fail(this, INVALID_KEY_ARGUMENT);
+ return new this.Collection(this, function () { return createRange(value, undefined, false); });
+ };
+ WhereClause.prototype.below = function (value) {
+ if (value == null)
+ return fail(this, INVALID_KEY_ARGUMENT);
+ return new this.Collection(this, function () { return createRange(undefined, value, false, true); });
+ };
+ WhereClause.prototype.belowOrEqual = function (value) {
+ if (value == null)
+ return fail(this, INVALID_KEY_ARGUMENT);
+ return new this.Collection(this, function () { return createRange(undefined, value); });
+ };
+ WhereClause.prototype.startsWith = function (str) {
+ if (typeof str !== 'string')
+ return fail(this, STRING_EXPECTED);
+ return this.between(str, str + maxString, true, true);
+ };
+ WhereClause.prototype.startsWithIgnoreCase = function (str) {
+ if (str === "")
+ return this.startsWith(str);
+ return addIgnoreCaseAlgorithm(this, function (x, a) { return x.indexOf(a[0]) === 0; }, [str], maxString);
+ };
+ WhereClause.prototype.equalsIgnoreCase = function (str) {
+ return addIgnoreCaseAlgorithm(this, function (x, a) { return x === a[0]; }, [str], "");
+ };
+ WhereClause.prototype.anyOfIgnoreCase = function () {
+ var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments);
+ if (set.length === 0)
+ return emptyCollection(this);
+ return addIgnoreCaseAlgorithm(this, function (x, a) { return a.indexOf(x) !== -1; }, set, "");
+ };
+ WhereClause.prototype.startsWithAnyOfIgnoreCase = function () {
+ var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments);
+ if (set.length === 0)
+ return emptyCollection(this);
+ return addIgnoreCaseAlgorithm(this, function (x, a) { return a.some(function (n) { return x.indexOf(n) === 0; }); }, set, maxString);
+ };
+ WhereClause.prototype.anyOf = function () {
+ var _this = this;
+ var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments);
+ var compare = this._cmp;
+ try {
+ set.sort(compare);
+ }
+ catch (e) {
+ return fail(this, INVALID_KEY_ARGUMENT);
+ }
+ if (set.length === 0)
+ return emptyCollection(this);
+ var c = new this.Collection(this, function () { return createRange(set[0], set[set.length - 1]); });
+ c._ondirectionchange = function (direction) {
+ compare = (direction === "next" ?
+ _this._ascending :
+ _this._descending);
+ set.sort(compare);
+ };
+ var i = 0;
+ c._addAlgorithm(function (cursor, advance, resolve) {
+ var key = cursor.key;
+ while (compare(key, set[i]) > 0) {
+ ++i;
+ if (i === set.length) {
+ advance(resolve);
+ return false;
+ }
+ }
+ if (compare(key, set[i]) === 0) {
+ return true;
+ }
+ else {
+ advance(function () { cursor.continue(set[i]); });
+ return false;
+ }
+ });
+ return c;
+ };
+ WhereClause.prototype.notEqual = function (value) {
+ return this.inAnyRange([[minKey, value], [value, this.db._maxKey]], { includeLowers: false, includeUppers: false });
+ };
+ WhereClause.prototype.noneOf = function () {
+ var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments);
+ if (set.length === 0)
+ return new this.Collection(this);
+ try {
+ set.sort(this._ascending);
+ }
+ catch (e) {
+ return fail(this, INVALID_KEY_ARGUMENT);
+ }
+ var ranges = set.reduce(function (res, val) { return res ?
+ res.concat([[res[res.length - 1][1], val]]) :
+ [[minKey, val]]; }, null);
+ ranges.push([set[set.length - 1], this.db._maxKey]);
+ return this.inAnyRange(ranges, { includeLowers: false, includeUppers: false });
+ };
+ WhereClause.prototype.inAnyRange = function (ranges, options) {
+ var _this = this;
+ var cmp = this._cmp, ascending = this._ascending, descending = this._descending, min = this._min, max = this._max;
+ if (ranges.length === 0)
+ return emptyCollection(this);
+ if (!ranges.every(function (range) {
+ return range[0] !== undefined &&
+ range[1] !== undefined &&
+ ascending(range[0], range[1]) <= 0;
+ })) {
+ return fail(this, "First argument to inAnyRange() must be an Array of two-value Arrays [lower,upper] where upper must not be lower than lower", exceptions.InvalidArgument);
+ }
+ var includeLowers = !options || options.includeLowers !== false;
+ var includeUppers = options && options.includeUppers === true;
+ function addRange(ranges, newRange) {
+ var i = 0, l = ranges.length;
+ for (; i < l; ++i) {
+ var range = ranges[i];
+ if (cmp(newRange[0], range[1]) < 0 && cmp(newRange[1], range[0]) > 0) {
+ range[0] = min(range[0], newRange[0]);
+ range[1] = max(range[1], newRange[1]);
+ break;
+ }
+ }
+ if (i === l)
+ ranges.push(newRange);
+ return ranges;
+ }
+ var sortDirection = ascending;
+ function rangeSorter(a, b) { return sortDirection(a[0], b[0]); }
+ var set;
+ try {
+ set = ranges.reduce(addRange, []);
+ set.sort(rangeSorter);
+ }
+ catch (ex) {
+ return fail(this, INVALID_KEY_ARGUMENT);
+ }
+ var rangePos = 0;
+ var keyIsBeyondCurrentEntry = includeUppers ?
+ function (key) { return ascending(key, set[rangePos][1]) > 0; } :
+ function (key) { return ascending(key, set[rangePos][1]) >= 0; };
+ var keyIsBeforeCurrentEntry = includeLowers ?
+ function (key) { return descending(key, set[rangePos][0]) > 0; } :
+ function (key) { return descending(key, set[rangePos][0]) >= 0; };
+ function keyWithinCurrentRange(key) {
+ return !keyIsBeyondCurrentEntry(key) && !keyIsBeforeCurrentEntry(key);
+ }
+ var checkKey = keyIsBeyondCurrentEntry;
+ var c = new this.Collection(this, function () { return createRange(set[0][0], set[set.length - 1][1], !includeLowers, !includeUppers); });
+ c._ondirectionchange = function (direction) {
+ if (direction === "next") {
+ checkKey = keyIsBeyondCurrentEntry;
+ sortDirection = ascending;
+ }
+ else {
+ checkKey = keyIsBeforeCurrentEntry;
+ sortDirection = descending;
+ }
+ set.sort(rangeSorter);
+ };
+ c._addAlgorithm(function (cursor, advance, resolve) {
+ var key = cursor.key;
+ while (checkKey(key)) {
+ ++rangePos;
+ if (rangePos === set.length) {
+ advance(resolve);
+ return false;
+ }
+ }
+ if (keyWithinCurrentRange(key)) {
+ return true;
+ }
+ else if (_this._cmp(key, set[rangePos][1]) === 0 || _this._cmp(key, set[rangePos][0]) === 0) {
+ return false;
+ }
+ else {
+ advance(function () {
+ if (sortDirection === ascending)
+ cursor.continue(set[rangePos][0]);
+ else
+ cursor.continue(set[rangePos][1]);
+ });
+ return false;
+ }
+ });
+ return c;
+ };
+ WhereClause.prototype.startsWithAnyOf = function () {
+ var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments);
+ if (!set.every(function (s) { return typeof s === 'string'; })) {
+ return fail(this, "startsWithAnyOf() only works with strings");
+ }
+ if (set.length === 0)
+ return emptyCollection(this);
+ return this.inAnyRange(set.map(function (str) { return [str, str + maxString]; }));
+ };
+ return WhereClause;
+ }());
+
+ function createWhereClauseConstructor(db) {
+ return makeClassConstructor(WhereClause.prototype, function WhereClause(table, index, orCollection) {
+ this.db = db;
+ this._ctx = {
+ table: table,
+ index: index === ":id" ? null : index,
+ or: orCollection
+ };
+ this._cmp = this._ascending = cmp;
+ this._descending = function (a, b) { return cmp(b, a); };
+ this._max = function (a, b) { return cmp(a, b) > 0 ? a : b; };
+ this._min = function (a, b) { return cmp(a, b) < 0 ? a : b; };
+ this._IDBKeyRange = db._deps.IDBKeyRange;
+ if (!this._IDBKeyRange)
+ throw new exceptions.MissingAPI();
+ });
+ }
+
+ function eventRejectHandler(reject) {
+ return wrap(function (event) {
+ preventDefault(event);
+ reject(event.target.error);
+ return false;
+ });
+ }
+ function preventDefault(event) {
+ if (event.stopPropagation)
+ event.stopPropagation();
+ if (event.preventDefault)
+ event.preventDefault();
+ }
+
+ var DEXIE_STORAGE_MUTATED_EVENT_NAME = 'storagemutated';
+ var STORAGE_MUTATED_DOM_EVENT_NAME = 'x-storagemutated-1';
+ var globalEvents = Events(null, DEXIE_STORAGE_MUTATED_EVENT_NAME);
+
+ var Transaction = (function () {
+ function Transaction() {
+ }
+ Transaction.prototype._lock = function () {
+ assert(!PSD.global);
+ ++this._reculock;
+ if (this._reculock === 1 && !PSD.global)
+ PSD.lockOwnerFor = this;
+ return this;
+ };
+ Transaction.prototype._unlock = function () {
+ assert(!PSD.global);
+ if (--this._reculock === 0) {
+ if (!PSD.global)
+ PSD.lockOwnerFor = null;
+ while (this._blockedFuncs.length > 0 && !this._locked()) {
+ var fnAndPSD = this._blockedFuncs.shift();
+ try {
+ usePSD(fnAndPSD[1], fnAndPSD[0]);
+ }
+ catch (e) { }
+ }
+ }
+ return this;
+ };
+ Transaction.prototype._locked = function () {
+ return this._reculock && PSD.lockOwnerFor !== this;
+ };
+ Transaction.prototype.create = function (idbtrans) {
+ var _this = this;
+ if (!this.mode)
+ return this;
+ var idbdb = this.db.idbdb;
+ var dbOpenError = this.db._state.dbOpenError;
+ assert(!this.idbtrans);
+ if (!idbtrans && !idbdb) {
+ switch (dbOpenError && dbOpenError.name) {
+ case "DatabaseClosedError":
+ throw new exceptions.DatabaseClosed(dbOpenError);
+ case "MissingAPIError":
+ throw new exceptions.MissingAPI(dbOpenError.message, dbOpenError);
+ default:
+ throw new exceptions.OpenFailed(dbOpenError);
+ }
+ }
+ if (!this.active)
+ throw new exceptions.TransactionInactive();
+ assert(this._completion._state === null);
+ idbtrans = this.idbtrans = idbtrans ||
+ (this.db.core
+ ? this.db.core.transaction(this.storeNames, this.mode, { durability: this.chromeTransactionDurability })
+ : idbdb.transaction(this.storeNames, this.mode, { durability: this.chromeTransactionDurability }));
+ idbtrans.onerror = wrap(function (ev) {
+ preventDefault(ev);
+ _this._reject(idbtrans.error);
+ });
+ idbtrans.onabort = wrap(function (ev) {
+ preventDefault(ev);
+ _this.active && _this._reject(new exceptions.Abort(idbtrans.error));
+ _this.active = false;
+ _this.on("abort").fire(ev);
+ });
+ idbtrans.oncomplete = wrap(function () {
+ _this.active = false;
+ _this._resolve();
+ if ('mutatedParts' in idbtrans) {
+ globalEvents.storagemutated.fire(idbtrans["mutatedParts"]);
+ }
+ });
+ return this;
+ };
+ Transaction.prototype._promise = function (mode, fn, bWriteLock) {
+ var _this = this;
+ if (mode === 'readwrite' && this.mode !== 'readwrite')
+ return rejection(new exceptions.ReadOnly("Transaction is readonly"));
+ if (!this.active)
+ return rejection(new exceptions.TransactionInactive());
+ if (this._locked()) {
+ return new DexiePromise(function (resolve, reject) {
+ _this._blockedFuncs.push([function () {
+ _this._promise(mode, fn, bWriteLock).then(resolve, reject);
+ }, PSD]);
+ });
+ }
+ else if (bWriteLock) {
+ return newScope(function () {
+ var p = new DexiePromise(function (resolve, reject) {
+ _this._lock();
+ var rv = fn(resolve, reject, _this);
+ if (rv && rv.then)
+ rv.then(resolve, reject);
+ });
+ p.finally(function () { return _this._unlock(); });
+ p._lib = true;
+ return p;
+ });
+ }
+ else {
+ var p = new DexiePromise(function (resolve, reject) {
+ var rv = fn(resolve, reject, _this);
+ if (rv && rv.then)
+ rv.then(resolve, reject);
+ });
+ p._lib = true;
+ return p;
+ }
+ };
+ Transaction.prototype._root = function () {
+ return this.parent ? this.parent._root() : this;
+ };
+ Transaction.prototype.waitFor = function (promiseLike) {
+ var root = this._root();
+ var promise = DexiePromise.resolve(promiseLike);
+ if (root._waitingFor) {
+ root._waitingFor = root._waitingFor.then(function () { return promise; });
+ }
+ else {
+ root._waitingFor = promise;
+ root._waitingQueue = [];
+ var store = root.idbtrans.objectStore(root.storeNames[0]);
+ (function spin() {
+ ++root._spinCount;
+ while (root._waitingQueue.length)
+ (root._waitingQueue.shift())();
+ if (root._waitingFor)
+ store.get(-Infinity).onsuccess = spin;
+ }());
+ }
+ var currentWaitPromise = root._waitingFor;
+ return new DexiePromise(function (resolve, reject) {
+ promise.then(function (res) { return root._waitingQueue.push(wrap(resolve.bind(null, res))); }, function (err) { return root._waitingQueue.push(wrap(reject.bind(null, err))); }).finally(function () {
+ if (root._waitingFor === currentWaitPromise) {
+ root._waitingFor = null;
+ }
+ });
+ });
+ };
+ Transaction.prototype.abort = function () {
+ if (this.active) {
+ this.active = false;
+ if (this.idbtrans)
+ this.idbtrans.abort();
+ this._reject(new exceptions.Abort());
+ }
+ };
+ Transaction.prototype.table = function (tableName) {
+ var memoizedTables = (this._memoizedTables || (this._memoizedTables = {}));
+ if (hasOwn(memoizedTables, tableName))
+ return memoizedTables[tableName];
+ var tableSchema = this.schema[tableName];
+ if (!tableSchema) {
+ throw new exceptions.NotFound("Table " + tableName + " not part of transaction");
+ }
+ var transactionBoundTable = new this.db.Table(tableName, tableSchema, this);
+ transactionBoundTable.core = this.db.core.table(tableName);
+ memoizedTables[tableName] = transactionBoundTable;
+ return transactionBoundTable;
+ };
+ return Transaction;
+ }());
+
+ function createTransactionConstructor(db) {
+ return makeClassConstructor(Transaction.prototype, function Transaction(mode, storeNames, dbschema, chromeTransactionDurability, parent) {
+ var _this = this;
+ if (mode !== 'readonly')
+ storeNames.forEach(function (storeName) {
+ var _a;
+ var yProps = (_a = dbschema[storeName]) === null || _a === void 0 ? void 0 : _a.yProps;
+ if (yProps)
+ storeNames = storeNames.concat(yProps.map(function (p) { return p.updatesTable; }));
+ });
+ this.db = db;
+ this.mode = mode;
+ this.storeNames = storeNames;
+ this.schema = dbschema;
+ this.chromeTransactionDurability = chromeTransactionDurability;
+ this.idbtrans = null;
+ this.on = Events(this, "complete", "error", "abort");
+ this.parent = parent || null;
+ this.active = true;
+ this._reculock = 0;
+ this._blockedFuncs = [];
+ this._resolve = null;
+ this._reject = null;
+ this._waitingFor = null;
+ this._waitingQueue = null;
+ this._spinCount = 0;
+ this._completion = new DexiePromise(function (resolve, reject) {
+ _this._resolve = resolve;
+ _this._reject = reject;
+ });
+ this._completion.then(function () {
+ _this.active = false;
+ _this.on.complete.fire();
+ }, function (e) {
+ var wasActive = _this.active;
+ _this.active = false;
+ _this.on.error.fire(e);
+ _this.parent ?
+ _this.parent._reject(e) :
+ wasActive && _this.idbtrans && _this.idbtrans.abort();
+ return rejection(e);
+ });
+ });
+ }
+
+ function createIndexSpec(name, keyPath, unique, multi, auto, compound, isPrimKey, type) {
+ return {
+ name: name,
+ keyPath: keyPath,
+ unique: unique,
+ multi: multi,
+ auto: auto,
+ compound: compound,
+ src: (unique && !isPrimKey ? '&' : '') + (multi ? '*' : '') + (auto ? "++" : "") + nameFromKeyPath(keyPath),
+ type: type
+ };
+ }
+ function nameFromKeyPath(keyPath) {
+ return typeof keyPath === 'string' ?
+ keyPath :
+ keyPath ? ('[' + [].join.call(keyPath, '+') + ']') : "";
+ }
+
+ function createTableSchema(name, primKey, indexes) {
+ return {
+ name: name,
+ primKey: primKey,
+ indexes: indexes,
+ mappedClass: null,
+ idxByName: arrayToObject(indexes, function (index) { return [index.name, index]; }),
+ };
+ }
+
+ function safariMultiStoreFix(storeNames) {
+ return storeNames.length === 1 ? storeNames[0] : storeNames;
+ }
+ var getMaxKey = function (IdbKeyRange) {
+ try {
+ IdbKeyRange.only([[]]);
+ getMaxKey = function () { return [[]]; };
+ return [[]];
+ }
+ catch (e) {
+ getMaxKey = function () { return maxString; };
+ return maxString;
+ }
+ };
+
+ function getKeyExtractor(keyPath) {
+ if (keyPath == null) {
+ return function () { return undefined; };
+ }
+ else if (typeof keyPath === 'string') {
+ return getSinglePathKeyExtractor(keyPath);
+ }
+ else {
+ return function (obj) { return getByKeyPath(obj, keyPath); };
+ }
+ }
+ function getSinglePathKeyExtractor(keyPath) {
+ var split = keyPath.split('.');
+ if (split.length === 1) {
+ return function (obj) { return obj[keyPath]; };
+ }
+ else {
+ return function (obj) { return getByKeyPath(obj, keyPath); };
+ }
+ }
+
+ function arrayify(arrayLike) {
+ return [].slice.call(arrayLike);
+ }
+ var _id_counter = 0;
+ function getKeyPathAlias(keyPath) {
+ return keyPath == null ?
+ ":id" :
+ typeof keyPath === 'string' ?
+ keyPath :
+ "[".concat(keyPath.join('+'), "]");
+ }
+ function createDBCore(db, IdbKeyRange, tmpTrans) {
+ function extractSchema(db, trans) {
+ var tables = arrayify(db.objectStoreNames);
+ return {
+ schema: {
+ name: db.name,
+ tables: tables.map(function (table) { return trans.objectStore(table); }).map(function (store) {
+ var keyPath = store.keyPath, autoIncrement = store.autoIncrement;
+ var compound = isArray(keyPath);
+ var outbound = keyPath == null;
+ var indexByKeyPath = {};
+ var result = {
+ name: store.name,
+ primaryKey: {
+ name: null,
+ isPrimaryKey: true,
+ outbound: outbound,
+ compound: compound,
+ keyPath: keyPath,
+ autoIncrement: autoIncrement,
+ unique: true,
+ extractKey: getKeyExtractor(keyPath)
+ },
+ indexes: arrayify(store.indexNames).map(function (indexName) { return store.index(indexName); })
+ .map(function (index) {
+ var name = index.name, unique = index.unique, multiEntry = index.multiEntry, keyPath = index.keyPath;
+ var compound = isArray(keyPath);
+ var result = {
+ name: name,
+ compound: compound,
+ keyPath: keyPath,
+ unique: unique,
+ multiEntry: multiEntry,
+ extractKey: getKeyExtractor(keyPath)
+ };
+ indexByKeyPath[getKeyPathAlias(keyPath)] = result;
+ return result;
+ }),
+ getIndexByKeyPath: function (keyPath) { return indexByKeyPath[getKeyPathAlias(keyPath)]; }
+ };
+ indexByKeyPath[":id"] = result.primaryKey;
+ if (keyPath != null) {
+ indexByKeyPath[getKeyPathAlias(keyPath)] = result.primaryKey;
+ }
+ return result;
+ })
+ },
+ hasGetAll: tables.length > 0 && ('getAll' in trans.objectStore(tables[0])) &&
+ !(typeof navigator !== 'undefined' && /Safari/.test(navigator.userAgent) &&
+ !/(Chrome\/|Edge\/)/.test(navigator.userAgent) &&
+ [].concat(navigator.userAgent.match(/Safari\/(\d*)/))[1] < 604)
+ };
+ }
+ function makeIDBKeyRange(range) {
+ if (range.type === 3 )
+ return null;
+ if (range.type === 4 )
+ throw new Error("Cannot convert never type to IDBKeyRange");
+ var lower = range.lower, upper = range.upper, lowerOpen = range.lowerOpen, upperOpen = range.upperOpen;
+ var idbRange = lower === undefined ?
+ upper === undefined ?
+ null :
+ IdbKeyRange.upperBound(upper, !!upperOpen) :
+ upper === undefined ?
+ IdbKeyRange.lowerBound(lower, !!lowerOpen) :
+ IdbKeyRange.bound(lower, upper, !!lowerOpen, !!upperOpen);
+ return idbRange;
+ }
+ function createDbCoreTable(tableSchema) {
+ var tableName = tableSchema.name;
+ function mutate(_a) {
+ var trans = _a.trans, type = _a.type, keys = _a.keys, values = _a.values, range = _a.range;
+ return new Promise(function (resolve, reject) {
+ resolve = wrap(resolve);
+ var store = trans.objectStore(tableName);
+ var outbound = store.keyPath == null;
+ var isAddOrPut = type === "put" || type === "add";
+ if (!isAddOrPut && type !== 'delete' && type !== 'deleteRange')
+ throw new Error("Invalid operation type: " + type);
+ var length = (keys || values || { length: 1 }).length;
+ if (keys && values && keys.length !== values.length) {
+ throw new Error("Given keys array must have same length as given values array.");
+ }
+ if (length === 0)
+ return resolve({ numFailures: 0, failures: {}, results: [], lastResult: undefined });
+ var req;
+ var reqs = [];
+ var failures = [];
+ var numFailures = 0;
+ var errorHandler = function (event) {
+ ++numFailures;
+ preventDefault(event);
+ };
+ if (type === 'deleteRange') {
+ if (range.type === 4 )
+ return resolve({ numFailures: numFailures, failures: failures, results: [], lastResult: undefined });
+ if (range.type === 3 )
+ reqs.push(req = store.clear());
+ else
+ reqs.push(req = store.delete(makeIDBKeyRange(range)));
+ }
+ else {
+ var _a = isAddOrPut ?
+ outbound ?
+ [values, keys] :
+ [values, null] :
+ [keys, null], args1 = _a[0], args2 = _a[1];
+ if (isAddOrPut) {
+ for (var i = 0; i < length; ++i) {
+ reqs.push(req = (args2 && args2[i] !== undefined ?
+ store[type](args1[i], args2[i]) :
+ store[type](args1[i])));
+ req.onerror = errorHandler;
+ }
+ }
+ else {
+ for (var i = 0; i < length; ++i) {
+ reqs.push(req = store[type](args1[i]));
+ req.onerror = errorHandler;
+ }
+ }
+ }
+ var done = function (event) {
+ var lastResult = event.target.result;
+ reqs.forEach(function (req, i) { return req.error != null && (failures[i] = req.error); });
+ resolve({
+ numFailures: numFailures,
+ failures: failures,
+ results: type === "delete" ? keys : reqs.map(function (req) { return req.result; }),
+ lastResult: lastResult
+ });
+ };
+ req.onerror = function (event) {
+ errorHandler(event);
+ done(event);
+ };
+ req.onsuccess = done;
+ });
+ }
+ function openCursor(_a) {
+ var trans = _a.trans, values = _a.values, query = _a.query, reverse = _a.reverse, unique = _a.unique;
+ return new Promise(function (resolve, reject) {
+ resolve = wrap(resolve);
+ var index = query.index, range = query.range;
+ var store = trans.objectStore(tableName);
+ var source = index.isPrimaryKey ?
+ store :
+ store.index(index.name);
+ var direction = reverse ?
+ unique ?
+ "prevunique" :
+ "prev" :
+ unique ?
+ "nextunique" :
+ "next";
+ var req = values || !('openKeyCursor' in source) ?
+ source.openCursor(makeIDBKeyRange(range), direction) :
+ source.openKeyCursor(makeIDBKeyRange(range), direction);
+ req.onerror = eventRejectHandler(reject);
+ req.onsuccess = wrap(function (ev) {
+ var cursor = req.result;
+ if (!cursor) {
+ resolve(null);
+ return;
+ }
+ cursor.___id = ++_id_counter;
+ cursor.done = false;
+ var _cursorContinue = cursor.continue.bind(cursor);
+ var _cursorContinuePrimaryKey = cursor.continuePrimaryKey;
+ if (_cursorContinuePrimaryKey)
+ _cursorContinuePrimaryKey = _cursorContinuePrimaryKey.bind(cursor);
+ var _cursorAdvance = cursor.advance.bind(cursor);
+ var doThrowCursorIsNotStarted = function () { throw new Error("Cursor not started"); };
+ var doThrowCursorIsStopped = function () { throw new Error("Cursor not stopped"); };
+ cursor.trans = trans;
+ cursor.stop = cursor.continue = cursor.continuePrimaryKey = cursor.advance = doThrowCursorIsNotStarted;
+ cursor.fail = wrap(reject);
+ cursor.next = function () {
+ var _this = this;
+ var gotOne = 1;
+ return this.start(function () { return gotOne-- ? _this.continue() : _this.stop(); }).then(function () { return _this; });
+ };
+ cursor.start = function (callback) {
+ var iterationPromise = new Promise(function (resolveIteration, rejectIteration) {
+ resolveIteration = wrap(resolveIteration);
+ req.onerror = eventRejectHandler(rejectIteration);
+ cursor.fail = rejectIteration;
+ cursor.stop = function (value) {
+ cursor.stop = cursor.continue = cursor.continuePrimaryKey = cursor.advance = doThrowCursorIsStopped;
+ resolveIteration(value);
+ };
+ });
+ var guardedCallback = function () {
+ if (req.result) {
+ try {
+ callback();
+ }
+ catch (err) {
+ cursor.fail(err);
+ }
+ }
+ else {
+ cursor.done = true;
+ cursor.start = function () { throw new Error("Cursor behind last entry"); };
+ cursor.stop();
+ }
+ };
+ req.onsuccess = wrap(function (ev) {
+ req.onsuccess = guardedCallback;
+ guardedCallback();
+ });
+ cursor.continue = _cursorContinue;
+ cursor.continuePrimaryKey = _cursorContinuePrimaryKey;
+ cursor.advance = _cursorAdvance;
+ guardedCallback();
+ return iterationPromise;
+ };
+ resolve(cursor);
+ }, reject);
+ });
+ }
+ function query(hasGetAll) {
+ return function (request) {
+ return new Promise(function (resolve, reject) {
+ resolve = wrap(resolve);
+ var trans = request.trans, values = request.values, limit = request.limit, query = request.query;
+ var nonInfinitLimit = limit === Infinity ? undefined : limit;
+ var index = query.index, range = query.range;
+ var store = trans.objectStore(tableName);
+ var source = index.isPrimaryKey ? store : store.index(index.name);
+ var idbKeyRange = makeIDBKeyRange(range);
+ if (limit === 0)
+ return resolve({ result: [] });
+ if (hasGetAll) {
+ var req = values ?
+ source.getAll(idbKeyRange, nonInfinitLimit) :
+ source.getAllKeys(idbKeyRange, nonInfinitLimit);
+ req.onsuccess = function (event) { return resolve({ result: event.target.result }); };
+ req.onerror = eventRejectHandler(reject);
+ }
+ else {
+ var count_1 = 0;
+ var req_1 = values || !('openKeyCursor' in source) ?
+ source.openCursor(idbKeyRange) :
+ source.openKeyCursor(idbKeyRange);
+ var result_1 = [];
+ req_1.onsuccess = function (event) {
+ var cursor = req_1.result;
+ if (!cursor)
+ return resolve({ result: result_1 });
+ result_1.push(values ? cursor.value : cursor.primaryKey);
+ if (++count_1 === limit)
+ return resolve({ result: result_1 });
+ cursor.continue();
+ };
+ req_1.onerror = eventRejectHandler(reject);
+ }
+ });
+ };
+ }
+ return {
+ name: tableName,
+ schema: tableSchema,
+ mutate: mutate,
+ getMany: function (_a) {
+ var trans = _a.trans, keys = _a.keys;
+ return new Promise(function (resolve, reject) {
+ resolve = wrap(resolve);
+ var store = trans.objectStore(tableName);
+ var length = keys.length;
+ var result = new Array(length);
+ var keyCount = 0;
+ var callbackCount = 0;
+ var req;
+ var successHandler = function (event) {
+ var req = event.target;
+ if ((result[req._pos] = req.result) != null)
+ ;
+ if (++callbackCount === keyCount)
+ resolve(result);
+ };
+ var errorHandler = eventRejectHandler(reject);
+ for (var i = 0; i < length; ++i) {
+ var key = keys[i];
+ if (key != null) {
+ req = store.get(keys[i]);
+ req._pos = i;
+ req.onsuccess = successHandler;
+ req.onerror = errorHandler;
+ ++keyCount;
+ }
+ }
+ if (keyCount === 0)
+ resolve(result);
+ });
+ },
+ get: function (_a) {
+ var trans = _a.trans, key = _a.key;
+ return new Promise(function (resolve, reject) {
+ resolve = wrap(resolve);
+ var store = trans.objectStore(tableName);
+ var req = store.get(key);
+ req.onsuccess = function (event) { return resolve(event.target.result); };
+ req.onerror = eventRejectHandler(reject);
+ });
+ },
+ query: query(hasGetAll),
+ openCursor: openCursor,
+ count: function (_a) {
+ var query = _a.query, trans = _a.trans;
+ var index = query.index, range = query.range;
+ return new Promise(function (resolve, reject) {
+ var store = trans.objectStore(tableName);
+ var source = index.isPrimaryKey ? store : store.index(index.name);
+ var idbKeyRange = makeIDBKeyRange(range);
+ var req = idbKeyRange ? source.count(idbKeyRange) : source.count();
+ req.onsuccess = wrap(function (ev) { return resolve(ev.target.result); });
+ req.onerror = eventRejectHandler(reject);
+ });
+ }
+ };
+ }
+ var _a = extractSchema(db, tmpTrans), schema = _a.schema, hasGetAll = _a.hasGetAll;
+ var tables = schema.tables.map(function (tableSchema) { return createDbCoreTable(tableSchema); });
+ var tableMap = {};
+ tables.forEach(function (table) { return tableMap[table.name] = table; });
+ return {
+ stack: "dbcore",
+ transaction: db.transaction.bind(db),
+ table: function (name) {
+ var result = tableMap[name];
+ if (!result)
+ throw new Error("Table '".concat(name, "' not found"));
+ return tableMap[name];
+ },
+ MIN_KEY: -Infinity,
+ MAX_KEY: getMaxKey(IdbKeyRange),
+ schema: schema
+ };
+ }
+
+ function createMiddlewareStack(stackImpl, middlewares) {
+ return middlewares.reduce(function (down, _a) {
+ var create = _a.create;
+ return (__assign(__assign({}, down), create(down)));
+ }, stackImpl);
+ }
+ function createMiddlewareStacks(middlewares, idbdb, _a, tmpTrans) {
+ var IDBKeyRange = _a.IDBKeyRange; _a.indexedDB;
+ var dbcore = createMiddlewareStack(createDBCore(idbdb, IDBKeyRange, tmpTrans), middlewares.dbcore);
+ return {
+ dbcore: dbcore
+ };
+ }
+ function generateMiddlewareStacks(db, tmpTrans) {
+ var idbdb = tmpTrans.db;
+ var stacks = createMiddlewareStacks(db._middlewares, idbdb, db._deps, tmpTrans);
+ db.core = stacks.dbcore;
+ db.tables.forEach(function (table) {
+ var tableName = table.name;
+ if (db.core.schema.tables.some(function (tbl) { return tbl.name === tableName; })) {
+ table.core = db.core.table(tableName);
+ if (db[tableName] instanceof db.Table) {
+ db[tableName].core = table.core;
+ }
+ }
+ });
+ }
+
+ function setApiOnPlace(db, objs, tableNames, dbschema) {
+ tableNames.forEach(function (tableName) {
+ var schema = dbschema[tableName];
+ objs.forEach(function (obj) {
+ var propDesc = getPropertyDescriptor(obj, tableName);
+ if (!propDesc || ("value" in propDesc && propDesc.value === undefined)) {
+ if (obj === db.Transaction.prototype || obj instanceof db.Transaction) {
+ setProp(obj, tableName, {
+ get: function () { return this.table(tableName); },
+ set: function (value) {
+ defineProperty(this, tableName, { value: value, writable: true, configurable: true, enumerable: true });
+ }
+ });
+ }
+ else {
+ obj[tableName] = new db.Table(tableName, schema);
+ }
+ }
+ });
+ });
+ }
+ function removeTablesApi(db, objs) {
+ objs.forEach(function (obj) {
+ for (var key in obj) {
+ if (obj[key] instanceof db.Table)
+ delete obj[key];
+ }
+ });
+ }
+ function lowerVersionFirst(a, b) {
+ return a._cfg.version - b._cfg.version;
+ }
+ function runUpgraders(db, oldVersion, idbUpgradeTrans, reject) {
+ var globalSchema = db._dbSchema;
+ if (idbUpgradeTrans.objectStoreNames.contains('$meta') && !globalSchema.$meta) {
+ globalSchema.$meta = createTableSchema("$meta", parseIndexSyntax("")[0], []);
+ db._storeNames.push('$meta');
+ }
+ var trans = db._createTransaction('readwrite', db._storeNames, globalSchema);
+ trans.create(idbUpgradeTrans);
+ trans._completion.catch(reject);
+ var rejectTransaction = trans._reject.bind(trans);
+ var transless = PSD.transless || PSD;
+ newScope(function () {
+ PSD.trans = trans;
+ PSD.transless = transless;
+ if (oldVersion === 0) {
+ keys(globalSchema).forEach(function (tableName) {
+ createTable(idbUpgradeTrans, tableName, globalSchema[tableName].primKey, globalSchema[tableName].indexes);
+ });
+ generateMiddlewareStacks(db, idbUpgradeTrans);
+ DexiePromise.follow(function () { return db.on.populate.fire(trans); }).catch(rejectTransaction);
+ }
+ else {
+ generateMiddlewareStacks(db, idbUpgradeTrans);
+ return getExistingVersion(db, trans, oldVersion)
+ .then(function (oldVersion) { return updateTablesAndIndexes(db, oldVersion, trans, idbUpgradeTrans); })
+ .catch(rejectTransaction);
+ }
+ });
+ }
+ function patchCurrentVersion(db, idbUpgradeTrans) {
+ createMissingTables(db._dbSchema, idbUpgradeTrans);
+ if (idbUpgradeTrans.db.version % 10 === 0 && !idbUpgradeTrans.objectStoreNames.contains('$meta')) {
+ idbUpgradeTrans.db.createObjectStore('$meta').add(Math.ceil((idbUpgradeTrans.db.version / 10) - 1), 'version');
+ }
+ var globalSchema = buildGlobalSchema(db, db.idbdb, idbUpgradeTrans);
+ adjustToExistingIndexNames(db, db._dbSchema, idbUpgradeTrans);
+ var diff = getSchemaDiff(globalSchema, db._dbSchema);
+ var _loop_1 = function (tableChange) {
+ if (tableChange.change.length || tableChange.recreate) {
+ console.warn("Unable to patch indexes of table ".concat(tableChange.name, " because it has changes on the type of index or primary key."));
+ return { value: void 0 };
+ }
+ var store = idbUpgradeTrans.objectStore(tableChange.name);
+ tableChange.add.forEach(function (idx) {
+ if (debug)
+ console.debug("Dexie upgrade patch: Creating missing index ".concat(tableChange.name, ".").concat(idx.src));
+ addIndex(store, idx);
+ });
+ };
+ for (var _i = 0, _a = diff.change; _i < _a.length; _i++) {
+ var tableChange = _a[_i];
+ var state_1 = _loop_1(tableChange);
+ if (typeof state_1 === "object")
+ return state_1.value;
+ }
+ }
+ function getExistingVersion(db, trans, oldVersion) {
+ if (trans.storeNames.includes('$meta')) {
+ return trans.table('$meta').get('version').then(function (metaVersion) {
+ return metaVersion != null ? metaVersion : oldVersion;
+ });
+ }
+ else {
+ return DexiePromise.resolve(oldVersion);
+ }
+ }
+ function updateTablesAndIndexes(db, oldVersion, trans, idbUpgradeTrans) {
+ var queue = [];
+ var versions = db._versions;
+ var globalSchema = db._dbSchema = buildGlobalSchema(db, db.idbdb, idbUpgradeTrans);
+ var versToRun = versions.filter(function (v) { return v._cfg.version >= oldVersion; });
+ if (versToRun.length === 0) {
+ return DexiePromise.resolve();
+ }
+ versToRun.forEach(function (version) {
+ queue.push(function () {
+ var oldSchema = globalSchema;
+ var newSchema = version._cfg.dbschema;
+ adjustToExistingIndexNames(db, oldSchema, idbUpgradeTrans);
+ adjustToExistingIndexNames(db, newSchema, idbUpgradeTrans);
+ globalSchema = db._dbSchema = newSchema;
+ var diff = getSchemaDiff(oldSchema, newSchema);
+ diff.add.forEach(function (tuple) {
+ createTable(idbUpgradeTrans, tuple[0], tuple[1].primKey, tuple[1].indexes);
+ });
+ diff.change.forEach(function (change) {
+ if (change.recreate) {
+ throw new exceptions.Upgrade("Not yet support for changing primary key");
+ }
+ else {
+ var store_1 = idbUpgradeTrans.objectStore(change.name);
+ change.add.forEach(function (idx) { return addIndex(store_1, idx); });
+ change.change.forEach(function (idx) {
+ store_1.deleteIndex(idx.name);
+ addIndex(store_1, idx);
+ });
+ change.del.forEach(function (idxName) { return store_1.deleteIndex(idxName); });
+ }
+ });
+ var contentUpgrade = version._cfg.contentUpgrade;
+ if (contentUpgrade && version._cfg.version > oldVersion) {
+ generateMiddlewareStacks(db, idbUpgradeTrans);
+ trans._memoizedTables = {};
+ var upgradeSchema_1 = shallowClone(newSchema);
+ diff.del.forEach(function (table) {
+ upgradeSchema_1[table] = oldSchema[table];
+ });
+ removeTablesApi(db, [db.Transaction.prototype]);
+ setApiOnPlace(db, [db.Transaction.prototype], keys(upgradeSchema_1), upgradeSchema_1);
+ trans.schema = upgradeSchema_1;
+ var contentUpgradeIsAsync_1 = isAsyncFunction(contentUpgrade);
+ if (contentUpgradeIsAsync_1) {
+ incrementExpectedAwaits();
+ }
+ var returnValue_1;
+ var promiseFollowed = DexiePromise.follow(function () {
+ returnValue_1 = contentUpgrade(trans);
+ if (returnValue_1) {
+ if (contentUpgradeIsAsync_1) {
+ var decrementor = decrementExpectedAwaits.bind(null, null);
+ returnValue_1.then(decrementor, decrementor);
+ }
+ }
+ });
+ return (returnValue_1 && typeof returnValue_1.then === 'function' ?
+ DexiePromise.resolve(returnValue_1) : promiseFollowed.then(function () { return returnValue_1; }));
+ }
+ });
+ queue.push(function (idbtrans) {
+ var newSchema = version._cfg.dbschema;
+ deleteRemovedTables(newSchema, idbtrans);
+ removeTablesApi(db, [db.Transaction.prototype]);
+ setApiOnPlace(db, [db.Transaction.prototype], db._storeNames, db._dbSchema);
+ trans.schema = db._dbSchema;
+ });
+ queue.push(function (idbtrans) {
+ if (db.idbdb.objectStoreNames.contains('$meta')) {
+ if (Math.ceil(db.idbdb.version / 10) === version._cfg.version) {
+ db.idbdb.deleteObjectStore('$meta');
+ delete db._dbSchema.$meta;
+ db._storeNames = db._storeNames.filter(function (name) { return name !== '$meta'; });
+ }
+ else {
+ idbtrans.objectStore('$meta').put(version._cfg.version, 'version');
+ }
+ }
+ });
+ });
+ function runQueue() {
+ return queue.length ? DexiePromise.resolve(queue.shift()(trans.idbtrans)).then(runQueue) :
+ DexiePromise.resolve();
+ }
+ return runQueue().then(function () {
+ createMissingTables(globalSchema, idbUpgradeTrans);
+ });
+ }
+ function getSchemaDiff(oldSchema, newSchema) {
+ var diff = {
+ del: [],
+ add: [],
+ change: []
+ };
+ var table;
+ for (table in oldSchema) {
+ if (!newSchema[table])
+ diff.del.push(table);
+ }
+ for (table in newSchema) {
+ var oldDef = oldSchema[table], newDef = newSchema[table];
+ if (!oldDef) {
+ diff.add.push([table, newDef]);
+ }
+ else {
+ var change = {
+ name: table,
+ def: newDef,
+ recreate: false,
+ del: [],
+ add: [],
+ change: []
+ };
+ if ((
+ '' + (oldDef.primKey.keyPath || '')) !== ('' + (newDef.primKey.keyPath || '')) ||
+ (oldDef.primKey.auto !== newDef.primKey.auto)) {
+ change.recreate = true;
+ diff.change.push(change);
+ }
+ else {
+ var oldIndexes = oldDef.idxByName;
+ var newIndexes = newDef.idxByName;
+ var idxName = void 0;
+ for (idxName in oldIndexes) {
+ if (!newIndexes[idxName])
+ change.del.push(idxName);
+ }
+ for (idxName in newIndexes) {
+ var oldIdx = oldIndexes[idxName], newIdx = newIndexes[idxName];
+ if (!oldIdx)
+ change.add.push(newIdx);
+ else if (oldIdx.src !== newIdx.src)
+ change.change.push(newIdx);
+ }
+ if (change.del.length > 0 || change.add.length > 0 || change.change.length > 0) {
+ diff.change.push(change);
+ }
+ }
+ }
+ }
+ return diff;
+ }
+ function createTable(idbtrans, tableName, primKey, indexes) {
+ var store = idbtrans.db.createObjectStore(tableName, primKey.keyPath ?
+ { keyPath: primKey.keyPath, autoIncrement: primKey.auto } :
+ { autoIncrement: primKey.auto });
+ indexes.forEach(function (idx) { return addIndex(store, idx); });
+ return store;
+ }
+ function createMissingTables(newSchema, idbtrans) {
+ keys(newSchema).forEach(function (tableName) {
+ if (!idbtrans.db.objectStoreNames.contains(tableName)) {
+ if (debug)
+ console.debug('Dexie: Creating missing table', tableName);
+ createTable(idbtrans, tableName, newSchema[tableName].primKey, newSchema[tableName].indexes);
+ }
+ });
+ }
+ function deleteRemovedTables(newSchema, idbtrans) {
+ [].slice.call(idbtrans.db.objectStoreNames).forEach(function (storeName) {
+ return newSchema[storeName] == null && idbtrans.db.deleteObjectStore(storeName);
+ });
+ }
+ function addIndex(store, idx) {
+ store.createIndex(idx.name, idx.keyPath, { unique: idx.unique, multiEntry: idx.multi });
+ }
+ function buildGlobalSchema(db, idbdb, tmpTrans) {
+ var globalSchema = {};
+ var dbStoreNames = slice(idbdb.objectStoreNames, 0);
+ dbStoreNames.forEach(function (storeName) {
+ var store = tmpTrans.objectStore(storeName);
+ var keyPath = store.keyPath;
+ var primKey = createIndexSpec(nameFromKeyPath(keyPath), keyPath || "", true, false, !!store.autoIncrement, keyPath && typeof keyPath !== "string", true);
+ var indexes = [];
+ for (var j = 0; j < store.indexNames.length; ++j) {
+ var idbindex = store.index(store.indexNames[j]);
+ keyPath = idbindex.keyPath;
+ var index = createIndexSpec(idbindex.name, keyPath, !!idbindex.unique, !!idbindex.multiEntry, false, keyPath && typeof keyPath !== "string", false);
+ indexes.push(index);
+ }
+ globalSchema[storeName] = createTableSchema(storeName, primKey, indexes);
+ });
+ return globalSchema;
+ }
+ function readGlobalSchema(db, idbdb, tmpTrans) {
+ db.verno = idbdb.version / 10;
+ var globalSchema = db._dbSchema = buildGlobalSchema(db, idbdb, tmpTrans);
+ db._storeNames = slice(idbdb.objectStoreNames, 0);
+ setApiOnPlace(db, [db._allTables], keys(globalSchema), globalSchema);
+ }
+ function verifyInstalledSchema(db, tmpTrans) {
+ var installedSchema = buildGlobalSchema(db, db.idbdb, tmpTrans);
+ var diff = getSchemaDiff(installedSchema, db._dbSchema);
+ return !(diff.add.length || diff.change.some(function (ch) { return ch.add.length || ch.change.length; }));
+ }
+ function adjustToExistingIndexNames(db, schema, idbtrans) {
+ var storeNames = idbtrans.db.objectStoreNames;
+ for (var i = 0; i < storeNames.length; ++i) {
+ var storeName = storeNames[i];
+ var store = idbtrans.objectStore(storeName);
+ db._hasGetAll = 'getAll' in store;
+ for (var j = 0; j < store.indexNames.length; ++j) {
+ var indexName = store.indexNames[j];
+ var keyPath = store.index(indexName).keyPath;
+ var dexieName = typeof keyPath === 'string' ? keyPath : "[" + slice(keyPath).join('+') + "]";
+ if (schema[storeName]) {
+ var indexSpec = schema[storeName].idxByName[dexieName];
+ if (indexSpec) {
+ indexSpec.name = indexName;
+ delete schema[storeName].idxByName[dexieName];
+ schema[storeName].idxByName[indexName] = indexSpec;
+ }
+ }
+ }
+ }
+ if (typeof navigator !== 'undefined' && /Safari/.test(navigator.userAgent) &&
+ !/(Chrome\/|Edge\/)/.test(navigator.userAgent) &&
+ _global.WorkerGlobalScope && _global instanceof _global.WorkerGlobalScope &&
+ [].concat(navigator.userAgent.match(/Safari\/(\d*)/))[1] < 604) {
+ db._hasGetAll = false;
+ }
+ }
+ function parseIndexSyntax(primKeyAndIndexes) {
+ return primKeyAndIndexes.split(',').map(function (index, indexNum) {
+ var _a;
+ var typeSplit = index.split(':');
+ var type = (_a = typeSplit[1]) === null || _a === void 0 ? void 0 : _a.trim();
+ index = typeSplit[0].trim();
+ var name = index.replace(/([&*]|\+\+)/g, "");
+ var keyPath = /^\[/.test(name) ? name.match(/^\[(.*)\]$/)[1].split('+') : name;
+ return createIndexSpec(name, keyPath || null, /\&/.test(index), /\*/.test(index), /\+\+/.test(index), isArray(keyPath), indexNum === 0, type);
+ });
+ }
+
+ var Version = (function () {
+ function Version() {
+ }
+ Version.prototype._createTableSchema = function (name, primKey, indexes) {
+ return createTableSchema(name, primKey, indexes);
+ };
+ Version.prototype._parseIndexSyntax = function (primKeyAndIndexes) {
+ return parseIndexSyntax(primKeyAndIndexes);
+ };
+ Version.prototype._parseStoresSpec = function (stores, outSchema) {
+ var _this = this;
+ keys(stores).forEach(function (tableName) {
+ if (stores[tableName] !== null) {
+ var indexes = _this._parseIndexSyntax(stores[tableName]);
+ var primKey = indexes.shift();
+ if (!primKey) {
+ throw new exceptions.Schema('Invalid schema for table ' + tableName + ': ' + stores[tableName]);
+ }
+ primKey.unique = true;
+ if (primKey.multi)
+ throw new exceptions.Schema('Primary key cannot be multiEntry*');
+ indexes.forEach(function (idx) {
+ if (idx.auto)
+ throw new exceptions.Schema('Only primary key can be marked as autoIncrement (++)');
+ if (!idx.keyPath)
+ throw new exceptions.Schema('Index must have a name and cannot be an empty string');
+ });
+ var tblSchema = _this._createTableSchema(tableName, primKey, indexes);
+ outSchema[tableName] = tblSchema;
+ }
+ });
+ };
+ Version.prototype.stores = function (stores) {
+ var db = this.db;
+ this._cfg.storesSource = this._cfg.storesSource
+ ? extend(this._cfg.storesSource, stores)
+ : stores;
+ var versions = db._versions;
+ var storesSpec = {};
+ var dbschema = {};
+ versions.forEach(function (version) {
+ extend(storesSpec, version._cfg.storesSource);
+ dbschema = version._cfg.dbschema = {};
+ version._parseStoresSpec(storesSpec, dbschema);
+ });
+ db._dbSchema = dbschema;
+ removeTablesApi(db, [db._allTables, db, db.Transaction.prototype]);
+ setApiOnPlace(db, [db._allTables, db, db.Transaction.prototype, this._cfg.tables], keys(dbschema), dbschema);
+ db._storeNames = keys(dbschema);
+ return this;
+ };
+ Version.prototype.upgrade = function (upgradeFunction) {
+ this._cfg.contentUpgrade = promisableChain(this._cfg.contentUpgrade || nop, upgradeFunction);
+ return this;
+ };
+ return Version;
+ }());
+
+ function createVersionConstructor(db) {
+ return makeClassConstructor(Version.prototype, function Version(versionNumber) {
+ this.db = db;
+ this._cfg = {
+ version: versionNumber,
+ storesSource: null,
+ dbschema: {},
+ tables: {},
+ contentUpgrade: null
+ };
+ });
+ }
+
+ function getDbNamesTable(indexedDB, IDBKeyRange) {
+ var dbNamesDB = indexedDB["_dbNamesDB"];
+ if (!dbNamesDB) {
+ dbNamesDB = indexedDB["_dbNamesDB"] = new Dexie$1(DBNAMES_DB, {
+ addons: [],
+ indexedDB: indexedDB,
+ IDBKeyRange: IDBKeyRange,
+ });
+ dbNamesDB.version(1).stores({ dbnames: "name" });
+ }
+ return dbNamesDB.table("dbnames");
+ }
+ function hasDatabasesNative(indexedDB) {
+ return indexedDB && typeof indexedDB.databases === "function";
+ }
+ function getDatabaseNames(_a) {
+ var indexedDB = _a.indexedDB, IDBKeyRange = _a.IDBKeyRange;
+ return hasDatabasesNative(indexedDB)
+ ? Promise.resolve(indexedDB.databases()).then(function (infos) {
+ return infos
+ .map(function (info) { return info.name; })
+ .filter(function (name) { return name !== DBNAMES_DB; });
+ })
+ : getDbNamesTable(indexedDB, IDBKeyRange).toCollection().primaryKeys();
+ }
+ function _onDatabaseCreated(_a, name) {
+ var indexedDB = _a.indexedDB, IDBKeyRange = _a.IDBKeyRange;
+ !hasDatabasesNative(indexedDB) &&
+ name !== DBNAMES_DB &&
+ getDbNamesTable(indexedDB, IDBKeyRange).put({ name: name }).catch(nop);
+ }
+ function _onDatabaseDeleted(_a, name) {
+ var indexedDB = _a.indexedDB, IDBKeyRange = _a.IDBKeyRange;
+ !hasDatabasesNative(indexedDB) &&
+ name !== DBNAMES_DB &&
+ getDbNamesTable(indexedDB, IDBKeyRange).delete(name).catch(nop);
+ }
+
+ function vip(fn) {
+ return newScope(function () {
+ PSD.letThrough = true;
+ return fn();
+ });
+ }
+
+ function idbReady() {
+ var isSafari = !navigator.userAgentData &&
+ /Safari\//.test(navigator.userAgent) &&
+ !/Chrom(e|ium)\//.test(navigator.userAgent);
+ if (!isSafari || !indexedDB.databases)
+ return Promise.resolve();
+ var intervalId;
+ return new Promise(function (resolve) {
+ var tryIdb = function () { return indexedDB.databases().finally(resolve); };
+ intervalId = setInterval(tryIdb, 100);
+ tryIdb();
+ }).finally(function () { return clearInterval(intervalId); });
+ }
+
+ var _a;
+ function isEmptyRange(node) {
+ return !("from" in node);
+ }
+ var RangeSet = function (fromOrTree, to) {
+ if (this) {
+ extend(this, arguments.length ? { d: 1, from: fromOrTree, to: arguments.length > 1 ? to : fromOrTree } : { d: 0 });
+ }
+ else {
+ var rv = new RangeSet();
+ if (fromOrTree && ("d" in fromOrTree)) {
+ extend(rv, fromOrTree);
+ }
+ return rv;
+ }
+ };
+ props(RangeSet.prototype, (_a = {
+ add: function (rangeSet) {
+ mergeRanges(this, rangeSet);
+ return this;
+ },
+ addKey: function (key) {
+ addRange(this, key, key);
+ return this;
+ },
+ addKeys: function (keys) {
+ var _this = this;
+ keys.forEach(function (key) { return addRange(_this, key, key); });
+ return this;
+ },
+ hasKey: function (key) {
+ var node = getRangeSetIterator(this).next(key).value;
+ return node && cmp(node.from, key) <= 0 && cmp(node.to, key) >= 0;
+ }
+ },
+ _a[iteratorSymbol] = function () {
+ return getRangeSetIterator(this);
+ },
+ _a));
+ function addRange(target, from, to) {
+ var diff = cmp(from, to);
+ if (isNaN(diff))
+ return;
+ if (diff > 0)
+ throw RangeError();
+ if (isEmptyRange(target))
+ return extend(target, { from: from, to: to, d: 1 });
+ var left = target.l;
+ var right = target.r;
+ if (cmp(to, target.from) < 0) {
+ left
+ ? addRange(left, from, to)
+ : (target.l = { from: from, to: to, d: 1, l: null, r: null });
+ return rebalance(target);
+ }
+ if (cmp(from, target.to) > 0) {
+ right
+ ? addRange(right, from, to)
+ : (target.r = { from: from, to: to, d: 1, l: null, r: null });
+ return rebalance(target);
+ }
+ if (cmp(from, target.from) < 0) {
+ target.from = from;
+ target.l = null;
+ target.d = right ? right.d + 1 : 1;
+ }
+ if (cmp(to, target.to) > 0) {
+ target.to = to;
+ target.r = null;
+ target.d = target.l ? target.l.d + 1 : 1;
+ }
+ var rightWasCutOff = !target.r;
+ if (left && !target.l) {
+ mergeRanges(target, left);
+ }
+ if (right && rightWasCutOff) {
+ mergeRanges(target, right);
+ }
+ }
+ function mergeRanges(target, newSet) {
+ function _addRangeSet(target, _a) {
+ var from = _a.from, to = _a.to, l = _a.l, r = _a.r;
+ addRange(target, from, to);
+ if (l)
+ _addRangeSet(target, l);
+ if (r)
+ _addRangeSet(target, r);
+ }
+ if (!isEmptyRange(newSet))
+ _addRangeSet(target, newSet);
+ }
+ function rangesOverlap(rangeSet1, rangeSet2) {
+ var i1 = getRangeSetIterator(rangeSet2);
+ var nextResult1 = i1.next();
+ if (nextResult1.done)
+ return false;
+ var a = nextResult1.value;
+ var i2 = getRangeSetIterator(rangeSet1);
+ var nextResult2 = i2.next(a.from);
+ var b = nextResult2.value;
+ while (!nextResult1.done && !nextResult2.done) {
+ if (cmp(b.from, a.to) <= 0 && cmp(b.to, a.from) >= 0)
+ return true;
+ cmp(a.from, b.from) < 0
+ ? (a = (nextResult1 = i1.next(b.from)).value)
+ : (b = (nextResult2 = i2.next(a.from)).value);
+ }
+ return false;
+ }
+ function getRangeSetIterator(node) {
+ var state = isEmptyRange(node) ? null : { s: 0, n: node };
+ return {
+ next: function (key) {
+ var keyProvided = arguments.length > 0;
+ while (state) {
+ switch (state.s) {
+ case 0:
+ state.s = 1;
+ if (keyProvided) {
+ while (state.n.l && cmp(key, state.n.from) < 0)
+ state = { up: state, n: state.n.l, s: 1 };
+ }
+ else {
+ while (state.n.l)
+ state = { up: state, n: state.n.l, s: 1 };
+ }
+ case 1:
+ state.s = 2;
+ if (!keyProvided || cmp(key, state.n.to) <= 0)
+ return { value: state.n, done: false };
+ case 2:
+ if (state.n.r) {
+ state.s = 3;
+ state = { up: state, n: state.n.r, s: 0 };
+ continue;
+ }
+ case 3:
+ state = state.up;
+ }
+ }
+ return { done: true };
+ },
+ };
+ }
+ function rebalance(target) {
+ var _a, _b;
+ var diff = (((_a = target.r) === null || _a === void 0 ? void 0 : _a.d) || 0) - (((_b = target.l) === null || _b === void 0 ? void 0 : _b.d) || 0);
+ var r = diff > 1 ? "r" : diff < -1 ? "l" : "";
+ if (r) {
+ var l = r === "r" ? "l" : "r";
+ var rootClone = __assign({}, target);
+ var oldRootRight = target[r];
+ target.from = oldRootRight.from;
+ target.to = oldRootRight.to;
+ target[r] = oldRootRight[r];
+ rootClone[r] = oldRootRight[l];
+ target[l] = rootClone;
+ rootClone.d = computeDepth(rootClone);
+ }
+ target.d = computeDepth(target);
+ }
+ function computeDepth(_a) {
+ var r = _a.r, l = _a.l;
+ return (r ? (l ? Math.max(r.d, l.d) : r.d) : l ? l.d : 0) + 1;
+ }
+
+ function extendObservabilitySet(target, newSet) {
+ keys(newSet).forEach(function (part) {
+ if (target[part])
+ mergeRanges(target[part], newSet[part]);
+ else
+ target[part] = cloneSimpleObjectTree(newSet[part]);
+ });
+ return target;
+ }
+
+ function obsSetsOverlap(os1, os2) {
+ return os1.all || os2.all || Object.keys(os1).some(function (key) { return os2[key] && rangesOverlap(os2[key], os1[key]); });
+ }
+
+ var cache = {};
+
+ var unsignaledParts = {};
+ var isTaskEnqueued = false;
+ function signalSubscribersLazily(part, optimistic) {
+ extendObservabilitySet(unsignaledParts, part);
+ if (!isTaskEnqueued) {
+ isTaskEnqueued = true;
+ setTimeout(function () {
+ isTaskEnqueued = false;
+ var parts = unsignaledParts;
+ unsignaledParts = {};
+ signalSubscribersNow(parts, false);
+ }, 0);
+ }
+ }
+ function signalSubscribersNow(updatedParts, deleteAffectedCacheEntries) {
+ if (deleteAffectedCacheEntries === void 0) { deleteAffectedCacheEntries = false; }
+ var queriesToSignal = new Set();
+ if (updatedParts.all) {
+ for (var _i = 0, _a = Object.values(cache); _i < _a.length; _i++) {
+ var tblCache = _a[_i];
+ collectTableSubscribers(tblCache, updatedParts, queriesToSignal, deleteAffectedCacheEntries);
+ }
+ }
+ else {
+ for (var key in updatedParts) {
+ var parts = /^idb\:\/\/(.*)\/(.*)\//.exec(key);
+ if (parts) {
+ var dbName = parts[1], tableName = parts[2];
+ var tblCache = cache["idb://".concat(dbName, "/").concat(tableName)];
+ if (tblCache)
+ collectTableSubscribers(tblCache, updatedParts, queriesToSignal, deleteAffectedCacheEntries);
+ }
+ }
+ }
+ queriesToSignal.forEach(function (requery) { return requery(); });
+ }
+ function collectTableSubscribers(tblCache, updatedParts, outQueriesToSignal, deleteAffectedCacheEntries) {
+ var updatedEntryLists = [];
+ for (var _i = 0, _a = Object.entries(tblCache.queries.query); _i < _a.length; _i++) {
+ var _b = _a[_i], indexName = _b[0], entries = _b[1];
+ var filteredEntries = [];
+ for (var _c = 0, entries_1 = entries; _c < entries_1.length; _c++) {
+ var entry = entries_1[_c];
+ if (obsSetsOverlap(updatedParts, entry.obsSet)) {
+ entry.subscribers.forEach(function (requery) { return outQueriesToSignal.add(requery); });
+ }
+ else if (deleteAffectedCacheEntries) {
+ filteredEntries.push(entry);
+ }
+ }
+ if (deleteAffectedCacheEntries)
+ updatedEntryLists.push([indexName, filteredEntries]);
+ }
+ if (deleteAffectedCacheEntries) {
+ for (var _d = 0, updatedEntryLists_1 = updatedEntryLists; _d < updatedEntryLists_1.length; _d++) {
+ var _e = updatedEntryLists_1[_d], indexName = _e[0], filteredEntries = _e[1];
+ tblCache.queries.query[indexName] = filteredEntries;
+ }
+ }
+ }
+
+ function dexieOpen(db) {
+ var state = db._state;
+ var indexedDB = db._deps.indexedDB;
+ if (state.isBeingOpened || db.idbdb)
+ return state.dbReadyPromise.then(function () { return state.dbOpenError ?
+ rejection(state.dbOpenError) :
+ db; });
+ state.isBeingOpened = true;
+ state.dbOpenError = null;
+ state.openComplete = false;
+ var openCanceller = state.openCanceller;
+ var nativeVerToOpen = Math.round(db.verno * 10);
+ var schemaPatchMode = false;
+ function throwIfCancelled() {
+ if (state.openCanceller !== openCanceller)
+ throw new exceptions.DatabaseClosed('db.open() was cancelled');
+ }
+ var resolveDbReady = state.dbReadyResolve,
+ upgradeTransaction = null, wasCreated = false;
+ var tryOpenDB = function () { return new DexiePromise(function (resolve, reject) {
+ throwIfCancelled();
+ if (!indexedDB)
+ throw new exceptions.MissingAPI();
+ var dbName = db.name;
+ var req = state.autoSchema || !nativeVerToOpen ?
+ indexedDB.open(dbName) :
+ indexedDB.open(dbName, nativeVerToOpen);
+ if (!req)
+ throw new exceptions.MissingAPI();
+ req.onerror = eventRejectHandler(reject);
+ req.onblocked = wrap(db._fireOnBlocked);
+ req.onupgradeneeded = wrap(function (e) {
+ upgradeTransaction = req.transaction;
+ if (state.autoSchema && !db._options.allowEmptyDB) {
+ req.onerror = preventDefault;
+ upgradeTransaction.abort();
+ req.result.close();
+ var delreq = indexedDB.deleteDatabase(dbName);
+ delreq.onsuccess = delreq.onerror = wrap(function () {
+ reject(new exceptions.NoSuchDatabase("Database ".concat(dbName, " doesnt exist")));
+ });
+ }
+ else {
+ upgradeTransaction.onerror = eventRejectHandler(reject);
+ var oldVer = e.oldVersion > Math.pow(2, 62) ? 0 : e.oldVersion;
+ wasCreated = oldVer < 1;
+ db.idbdb = req.result;
+ if (schemaPatchMode) {
+ patchCurrentVersion(db, upgradeTransaction);
+ }
+ runUpgraders(db, oldVer / 10, upgradeTransaction, reject);
+ }
+ }, reject);
+ req.onsuccess = wrap(function () {
+ upgradeTransaction = null;
+ var idbdb = db.idbdb = req.result;
+ var objectStoreNames = slice(idbdb.objectStoreNames);
+ if (objectStoreNames.length > 0)
+ try {
+ var tmpTrans = idbdb.transaction(safariMultiStoreFix(objectStoreNames), 'readonly');
+ if (state.autoSchema)
+ readGlobalSchema(db, idbdb, tmpTrans);
+ else {
+ adjustToExistingIndexNames(db, db._dbSchema, tmpTrans);
+ if (!verifyInstalledSchema(db, tmpTrans) && !schemaPatchMode) {
+ console.warn("Dexie SchemaDiff: Schema was extended without increasing the number passed to db.version(). Dexie will add missing parts and increment native version number to workaround this.");
+ idbdb.close();
+ nativeVerToOpen = idbdb.version + 1;
+ schemaPatchMode = true;
+ return resolve(tryOpenDB());
+ }
+ }
+ generateMiddlewareStacks(db, tmpTrans);
+ }
+ catch (e) {
+ }
+ connections.push(db);
+ idbdb.onversionchange = wrap(function (ev) {
+ state.vcFired = true;
+ db.on("versionchange").fire(ev);
+ });
+ idbdb.onclose = wrap(function () {
+ db.close({ disableAutoOpen: false });
+ });
+ if (wasCreated)
+ _onDatabaseCreated(db._deps, dbName);
+ resolve();
+ }, reject);
+ }).catch(function (err) {
+ switch (err === null || err === void 0 ? void 0 : err.name) {
+ case "UnknownError":
+ if (state.PR1398_maxLoop > 0) {
+ state.PR1398_maxLoop--;
+ console.warn('Dexie: Workaround for Chrome UnknownError on open()');
+ return tryOpenDB();
+ }
+ break;
+ case "VersionError":
+ if (nativeVerToOpen > 0) {
+ nativeVerToOpen = 0;
+ return tryOpenDB();
+ }
+ break;
+ }
+ return DexiePromise.reject(err);
+ }); };
+ return DexiePromise.race([
+ openCanceller,
+ (typeof navigator === 'undefined' ? DexiePromise.resolve() : idbReady()).then(tryOpenDB)
+ ]).then(function () {
+ throwIfCancelled();
+ state.onReadyBeingFired = [];
+ return DexiePromise.resolve(vip(function () { return db.on.ready.fire(db.vip); })).then(function fireRemainders() {
+ if (state.onReadyBeingFired.length > 0) {
+ var remainders_1 = state.onReadyBeingFired.reduce(promisableChain, nop);
+ state.onReadyBeingFired = [];
+ return DexiePromise.resolve(vip(function () { return remainders_1(db.vip); })).then(fireRemainders);
+ }
+ });
+ }).finally(function () {
+ if (state.openCanceller === openCanceller) {
+ state.onReadyBeingFired = null;
+ state.isBeingOpened = false;
+ }
+ }).catch(function (err) {
+ state.dbOpenError = err;
+ try {
+ upgradeTransaction && upgradeTransaction.abort();
+ }
+ catch (_a) { }
+ if (openCanceller === state.openCanceller) {
+ db._close();
+ }
+ return rejection(err);
+ }).finally(function () {
+ state.openComplete = true;
+ resolveDbReady();
+ }).then(function () {
+ if (wasCreated) {
+ var everything_1 = {};
+ db.tables.forEach(function (table) {
+ table.schema.indexes.forEach(function (idx) {
+ if (idx.name)
+ everything_1["idb://".concat(db.name, "/").concat(table.name, "/").concat(idx.name)] = new RangeSet(-Infinity, [[[]]]);
+ });
+ everything_1["idb://".concat(db.name, "/").concat(table.name, "/")] = everything_1["idb://".concat(db.name, "/").concat(table.name, "/:dels")] = new RangeSet(-Infinity, [[[]]]);
+ });
+ globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME).fire(everything_1);
+ signalSubscribersNow(everything_1, true);
+ }
+ return db;
+ });
+ }
+
+ function awaitIterator(iterator) {
+ var callNext = function (result) { return iterator.next(result); }, doThrow = function (error) { return iterator.throw(error); }, onSuccess = step(callNext), onError = step(doThrow);
+ function step(getNext) {
+ return function (val) {
+ var next = getNext(val), value = next.value;
+ return next.done ? value :
+ (!value || typeof value.then !== 'function' ?
+ isArray(value) ? Promise.all(value).then(onSuccess, onError) : onSuccess(value) :
+ value.then(onSuccess, onError));
+ };
+ }
+ return step(callNext)();
+ }
+
+ function extractTransactionArgs(mode, _tableArgs_, scopeFunc) {
+ var i = arguments.length;
+ if (i < 2)
+ throw new exceptions.InvalidArgument("Too few arguments");
+ var args = new Array(i - 1);
+ while (--i)
+ args[i - 1] = arguments[i];
+ scopeFunc = args.pop();
+ var tables = flatten(args);
+ return [mode, tables, scopeFunc];
+ }
+ function enterTransactionScope(db, mode, storeNames, parentTransaction, scopeFunc) {
+ return DexiePromise.resolve().then(function () {
+ var transless = PSD.transless || PSD;
+ var trans = db._createTransaction(mode, storeNames, db._dbSchema, parentTransaction);
+ trans.explicit = true;
+ var zoneProps = {
+ trans: trans,
+ transless: transless
+ };
+ if (parentTransaction) {
+ trans.idbtrans = parentTransaction.idbtrans;
+ }
+ else {
+ try {
+ trans.create();
+ trans.idbtrans._explicit = true;
+ db._state.PR1398_maxLoop = 3;
+ }
+ catch (ex) {
+ if (ex.name === errnames.InvalidState && db.isOpen() && --db._state.PR1398_maxLoop > 0) {
+ console.warn('Dexie: Need to reopen db');
+ db.close({ disableAutoOpen: false });
+ return db.open().then(function () { return enterTransactionScope(db, mode, storeNames, null, scopeFunc); });
+ }
+ return rejection(ex);
+ }
+ }
+ var scopeFuncIsAsync = isAsyncFunction(scopeFunc);
+ if (scopeFuncIsAsync) {
+ incrementExpectedAwaits();
+ }
+ var returnValue;
+ var promiseFollowed = DexiePromise.follow(function () {
+ returnValue = scopeFunc.call(trans, trans);
+ if (returnValue) {
+ if (scopeFuncIsAsync) {
+ var decrementor = decrementExpectedAwaits.bind(null, null);
+ returnValue.then(decrementor, decrementor);
+ }
+ else if (typeof returnValue.next === 'function' && typeof returnValue.throw === 'function') {
+ returnValue = awaitIterator(returnValue);
+ }
+ }
+ }, zoneProps);
+ return (returnValue && typeof returnValue.then === 'function' ?
+ DexiePromise.resolve(returnValue).then(function (x) { return trans.active ?
+ x
+ : rejection(new exceptions.PrematureCommit("Transaction committed too early. See http://bit.ly/2kdckMn")); })
+ : promiseFollowed.then(function () { return returnValue; })).then(function (x) {
+ if (parentTransaction)
+ trans._resolve();
+ return trans._completion.then(function () { return x; });
+ }).catch(function (e) {
+ trans._reject(e);
+ return rejection(e);
+ });
+ });
+ }
+
+ function pad(a, value, count) {
+ var result = isArray(a) ? a.slice() : [a];
+ for (var i = 0; i < count; ++i)
+ result.push(value);
+ return result;
+ }
+ function createVirtualIndexMiddleware(down) {
+ return __assign(__assign({}, down), { table: function (tableName) {
+ var table = down.table(tableName);
+ var schema = table.schema;
+ var indexLookup = {};
+ var allVirtualIndexes = [];
+ function addVirtualIndexes(keyPath, keyTail, lowLevelIndex) {
+ var keyPathAlias = getKeyPathAlias(keyPath);
+ var indexList = (indexLookup[keyPathAlias] = indexLookup[keyPathAlias] || []);
+ var keyLength = keyPath == null ? 0 : typeof keyPath === 'string' ? 1 : keyPath.length;
+ var isVirtual = keyTail > 0;
+ var virtualIndex = __assign(__assign({}, lowLevelIndex), { name: isVirtual
+ ? "".concat(keyPathAlias, "(virtual-from:").concat(lowLevelIndex.name, ")")
+ : lowLevelIndex.name, lowLevelIndex: lowLevelIndex, isVirtual: isVirtual, keyTail: keyTail, keyLength: keyLength, extractKey: getKeyExtractor(keyPath), unique: !isVirtual && lowLevelIndex.unique });
+ indexList.push(virtualIndex);
+ if (!virtualIndex.isPrimaryKey) {
+ allVirtualIndexes.push(virtualIndex);
+ }
+ if (keyLength > 1) {
+ var virtualKeyPath = keyLength === 2 ?
+ keyPath[0] :
+ keyPath.slice(0, keyLength - 1);
+ addVirtualIndexes(virtualKeyPath, keyTail + 1, lowLevelIndex);
+ }
+ indexList.sort(function (a, b) { return a.keyTail - b.keyTail; });
+ return virtualIndex;
+ }
+ var primaryKey = addVirtualIndexes(schema.primaryKey.keyPath, 0, schema.primaryKey);
+ indexLookup[":id"] = [primaryKey];
+ for (var _i = 0, _a = schema.indexes; _i < _a.length; _i++) {
+ var index = _a[_i];
+ addVirtualIndexes(index.keyPath, 0, index);
+ }
+ function findBestIndex(keyPath) {
+ var result = indexLookup[getKeyPathAlias(keyPath)];
+ return result && result[0];
+ }
+ function translateRange(range, keyTail) {
+ return {
+ type: range.type === 1 ?
+ 2 :
+ range.type,
+ lower: pad(range.lower, range.lowerOpen ? down.MAX_KEY : down.MIN_KEY, keyTail),
+ lowerOpen: true,
+ upper: pad(range.upper, range.upperOpen ? down.MIN_KEY : down.MAX_KEY, keyTail),
+ upperOpen: true
+ };
+ }
+ function translateRequest(req) {
+ var index = req.query.index;
+ return index.isVirtual ? __assign(__assign({}, req), { query: {
+ index: index.lowLevelIndex,
+ range: translateRange(req.query.range, index.keyTail)
+ } }) : req;
+ }
+ var result = __assign(__assign({}, table), { schema: __assign(__assign({}, schema), { primaryKey: primaryKey, indexes: allVirtualIndexes, getIndexByKeyPath: findBestIndex }), count: function (req) {
+ return table.count(translateRequest(req));
+ }, query: function (req) {
+ return table.query(translateRequest(req));
+ }, openCursor: function (req) {
+ var _a = req.query.index, keyTail = _a.keyTail, isVirtual = _a.isVirtual, keyLength = _a.keyLength;
+ if (!isVirtual)
+ return table.openCursor(req);
+ function createVirtualCursor(cursor) {
+ function _continue(key) {
+ key != null ?
+ cursor.continue(pad(key, req.reverse ? down.MAX_KEY : down.MIN_KEY, keyTail)) :
+ req.unique ?
+ cursor.continue(cursor.key.slice(0, keyLength)
+ .concat(req.reverse
+ ? down.MIN_KEY
+ : down.MAX_KEY, keyTail)) :
+ cursor.continue();
+ }
+ var virtualCursor = Object.create(cursor, {
+ continue: { value: _continue },
+ continuePrimaryKey: {
+ value: function (key, primaryKey) {
+ cursor.continuePrimaryKey(pad(key, down.MAX_KEY, keyTail), primaryKey);
+ }
+ },
+ primaryKey: {
+ get: function () {
+ return cursor.primaryKey;
+ }
+ },
+ key: {
+ get: function () {
+ var key = cursor.key;
+ return keyLength === 1 ?
+ key[0] :
+ key.slice(0, keyLength);
+ }
+ },
+ value: {
+ get: function () {
+ return cursor.value;
+ }
+ }
+ });
+ return virtualCursor;
+ }
+ return table.openCursor(translateRequest(req))
+ .then(function (cursor) { return cursor && createVirtualCursor(cursor); });
+ } });
+ return result;
+ } });
+ }
+ var virtualIndexMiddleware = {
+ stack: "dbcore",
+ name: "VirtualIndexMiddleware",
+ level: 1,
+ create: createVirtualIndexMiddleware
+ };
+
+ function getObjectDiff(a, b, rv, prfx) {
+ rv = rv || {};
+ prfx = prfx || '';
+ keys(a).forEach(function (prop) {
+ if (!hasOwn(b, prop)) {
+ rv[prfx + prop] = undefined;
+ }
+ else {
+ var ap = a[prop], bp = b[prop];
+ if (typeof ap === 'object' && typeof bp === 'object' && ap && bp) {
+ var apTypeName = toStringTag(ap);
+ var bpTypeName = toStringTag(bp);
+ if (apTypeName !== bpTypeName) {
+ rv[prfx + prop] = b[prop];
+ }
+ else if (apTypeName === 'Object') {
+ getObjectDiff(ap, bp, rv, prfx + prop + '.');
+ }
+ else if (ap !== bp) {
+ rv[prfx + prop] = b[prop];
+ }
+ }
+ else if (ap !== bp)
+ rv[prfx + prop] = b[prop];
+ }
+ });
+ keys(b).forEach(function (prop) {
+ if (!hasOwn(a, prop)) {
+ rv[prfx + prop] = b[prop];
+ }
+ });
+ return rv;
+ }
+
+ function getEffectiveKeys(primaryKey, req) {
+ if (req.type === 'delete')
+ return req.keys;
+ return req.keys || req.values.map(primaryKey.extractKey);
+ }
+
+ var hooksMiddleware = {
+ stack: "dbcore",
+ name: "HooksMiddleware",
+ level: 2,
+ create: function (downCore) { return (__assign(__assign({}, downCore), { table: function (tableName) {
+ var downTable = downCore.table(tableName);
+ var primaryKey = downTable.schema.primaryKey;
+ var tableMiddleware = __assign(__assign({}, downTable), { mutate: function (req) {
+ var dxTrans = PSD.trans;
+ var _a = dxTrans.table(tableName).hook, deleting = _a.deleting, creating = _a.creating, updating = _a.updating;
+ switch (req.type) {
+ case 'add':
+ if (creating.fire === nop)
+ break;
+ return dxTrans._promise('readwrite', function () { return addPutOrDelete(req); }, true);
+ case 'put':
+ if (creating.fire === nop && updating.fire === nop)
+ break;
+ return dxTrans._promise('readwrite', function () { return addPutOrDelete(req); }, true);
+ case 'delete':
+ if (deleting.fire === nop)
+ break;
+ return dxTrans._promise('readwrite', function () { return addPutOrDelete(req); }, true);
+ case 'deleteRange':
+ if (deleting.fire === nop)
+ break;
+ return dxTrans._promise('readwrite', function () { return deleteRange(req); }, true);
+ }
+ return downTable.mutate(req);
+ function addPutOrDelete(req) {
+ var dxTrans = PSD.trans;
+ var keys = req.keys || getEffectiveKeys(primaryKey, req);
+ if (!keys)
+ throw new Error("Keys missing");
+ req = req.type === 'add' || req.type === 'put' ? __assign(__assign({}, req), { keys: keys }) : __assign({}, req);
+ if (req.type !== 'delete')
+ req.values = __spreadArray([], req.values, true);
+ if (req.keys)
+ req.keys = __spreadArray([], req.keys, true);
+ return getExistingValues(downTable, req, keys).then(function (existingValues) {
+ var contexts = keys.map(function (key, i) {
+ var existingValue = existingValues[i];
+ var ctx = { onerror: null, onsuccess: null };
+ if (req.type === 'delete') {
+ deleting.fire.call(ctx, key, existingValue, dxTrans);
+ }
+ else if (req.type === 'add' || existingValue === undefined) {
+ var generatedPrimaryKey = creating.fire.call(ctx, key, req.values[i], dxTrans);
+ if (key == null && generatedPrimaryKey != null) {
+ key = generatedPrimaryKey;
+ req.keys[i] = key;
+ if (!primaryKey.outbound) {
+ setByKeyPath(req.values[i], primaryKey.keyPath, key);
+ }
+ }
+ }
+ else {
+ var objectDiff = getObjectDiff(existingValue, req.values[i]);
+ var additionalChanges_1 = updating.fire.call(ctx, objectDiff, key, existingValue, dxTrans);
+ if (additionalChanges_1) {
+ var requestedValue_1 = req.values[i];
+ Object.keys(additionalChanges_1).forEach(function (keyPath) {
+ if (hasOwn(requestedValue_1, keyPath)) {
+ requestedValue_1[keyPath] = additionalChanges_1[keyPath];
+ }
+ else {
+ setByKeyPath(requestedValue_1, keyPath, additionalChanges_1[keyPath]);
+ }
+ });
+ }
+ }
+ return ctx;
+ });
+ return downTable.mutate(req).then(function (_a) {
+ var failures = _a.failures, results = _a.results, numFailures = _a.numFailures, lastResult = _a.lastResult;
+ for (var i = 0; i < keys.length; ++i) {
+ var primKey = results ? results[i] : keys[i];
+ var ctx = contexts[i];
+ if (primKey == null) {
+ ctx.onerror && ctx.onerror(failures[i]);
+ }
+ else {
+ ctx.onsuccess && ctx.onsuccess(req.type === 'put' && existingValues[i] ?
+ req.values[i] :
+ primKey
+ );
+ }
+ }
+ return { failures: failures, results: results, numFailures: numFailures, lastResult: lastResult };
+ }).catch(function (error) {
+ contexts.forEach(function (ctx) { return ctx.onerror && ctx.onerror(error); });
+ return Promise.reject(error);
+ });
+ });
+ }
+ function deleteRange(req) {
+ return deleteNextChunk(req.trans, req.range, 10000);
+ }
+ function deleteNextChunk(trans, range, limit) {
+ return downTable.query({ trans: trans, values: false, query: { index: primaryKey, range: range }, limit: limit })
+ .then(function (_a) {
+ var result = _a.result;
+ return addPutOrDelete({ type: 'delete', keys: result, trans: trans }).then(function (res) {
+ if (res.numFailures > 0)
+ return Promise.reject(res.failures[0]);
+ if (result.length < limit) {
+ return { failures: [], numFailures: 0, lastResult: undefined };
+ }
+ else {
+ return deleteNextChunk(trans, __assign(__assign({}, range), { lower: result[result.length - 1], lowerOpen: true }), limit);
+ }
+ });
+ });
+ }
+ } });
+ return tableMiddleware;
+ } })); }
+ };
+ function getExistingValues(table, req, effectiveKeys) {
+ return req.type === "add"
+ ? Promise.resolve([])
+ : table.getMany({ trans: req.trans, keys: effectiveKeys, cache: "immutable" });
+ }
+
+ function getFromTransactionCache(keys, cache, clone) {
+ try {
+ if (!cache)
+ return null;
+ if (cache.keys.length < keys.length)
+ return null;
+ var result = [];
+ for (var i = 0, j = 0; i < cache.keys.length && j < keys.length; ++i) {
+ if (cmp(cache.keys[i], keys[j]) !== 0)
+ continue;
+ result.push(clone ? deepClone(cache.values[i]) : cache.values[i]);
+ ++j;
+ }
+ return result.length === keys.length ? result : null;
+ }
+ catch (_a) {
+ return null;
+ }
+ }
+ var cacheExistingValuesMiddleware = {
+ stack: "dbcore",
+ level: -1,
+ create: function (core) {
+ return {
+ table: function (tableName) {
+ var table = core.table(tableName);
+ return __assign(__assign({}, table), { getMany: function (req) {
+ if (!req.cache) {
+ return table.getMany(req);
+ }
+ var cachedResult = getFromTransactionCache(req.keys, req.trans["_cache"], req.cache === "clone");
+ if (cachedResult) {
+ return DexiePromise.resolve(cachedResult);
+ }
+ return table.getMany(req).then(function (res) {
+ req.trans["_cache"] = {
+ keys: req.keys,
+ values: req.cache === "clone" ? deepClone(res) : res,
+ };
+ return res;
+ });
+ }, mutate: function (req) {
+ if (req.type !== "add")
+ req.trans["_cache"] = null;
+ return table.mutate(req);
+ } });
+ },
+ };
+ },
+ };
+
+ function isCachableContext(ctx, table) {
+ return (ctx.trans.mode === 'readonly' &&
+ !!ctx.subscr &&
+ !ctx.trans.explicit &&
+ ctx.trans.db._options.cache !== 'disabled' &&
+ !table.schema.primaryKey.outbound);
+ }
+
+ function isCachableRequest(type, req) {
+ switch (type) {
+ case 'query':
+ return req.values && !req.unique;
+ case 'get':
+ return false;
+ case 'getMany':
+ return false;
+ case 'count':
+ return false;
+ case 'openCursor':
+ return false;
+ }
+ }
+
+ var observabilityMiddleware = {
+ stack: "dbcore",
+ level: 0,
+ name: "Observability",
+ create: function (core) {
+ var dbName = core.schema.name;
+ var FULL_RANGE = new RangeSet(core.MIN_KEY, core.MAX_KEY);
+ return __assign(__assign({}, core), { transaction: function (stores, mode, options) {
+ if (PSD.subscr && mode !== 'readonly') {
+ throw new exceptions.ReadOnly("Readwrite transaction in liveQuery context. Querier source: ".concat(PSD.querier));
+ }
+ return core.transaction(stores, mode, options);
+ }, table: function (tableName) {
+ var table = core.table(tableName);
+ var schema = table.schema;
+ var primaryKey = schema.primaryKey, indexes = schema.indexes;
+ var extractKey = primaryKey.extractKey, outbound = primaryKey.outbound;
+ var indexesWithAutoIncPK = primaryKey.autoIncrement && indexes.filter(function (index) { return index.compound && index.keyPath.includes(primaryKey.keyPath); });
+ var tableClone = __assign(__assign({}, table), { mutate: function (req) {
+ var _a, _b;
+ var trans = req.trans;
+ var mutatedParts = req.mutatedParts || (req.mutatedParts = {});
+ var getRangeSet = function (indexName) {
+ var part = "idb://".concat(dbName, "/").concat(tableName, "/").concat(indexName);
+ return (mutatedParts[part] ||
+ (mutatedParts[part] = new RangeSet()));
+ };
+ var pkRangeSet = getRangeSet("");
+ var delsRangeSet = getRangeSet(":dels");
+ var type = req.type;
+ var _c = req.type === "deleteRange"
+ ? [req.range]
+ : req.type === "delete"
+ ? [req.keys]
+ : req.values.length < 50
+ ? [getEffectiveKeys(primaryKey, req).filter(function (id) { return id; }), req.values]
+ : [], keys = _c[0], newObjs = _c[1];
+ var oldCache = req.trans["_cache"];
+ if (isArray(keys)) {
+ pkRangeSet.addKeys(keys);
+ var oldObjs = type === 'delete' || keys.length === newObjs.length ? getFromTransactionCache(keys, oldCache) : null;
+ if (!oldObjs) {
+ delsRangeSet.addKeys(keys);
+ }
+ if (oldObjs || newObjs) {
+ trackAffectedIndexes(getRangeSet, schema, oldObjs, newObjs);
+ }
+ }
+ else if (keys) {
+ var range = {
+ from: (_a = keys.lower) !== null && _a !== void 0 ? _a : core.MIN_KEY,
+ to: (_b = keys.upper) !== null && _b !== void 0 ? _b : core.MAX_KEY
+ };
+ delsRangeSet.add(range);
+ pkRangeSet.add(range);
+ }
+ else {
+ pkRangeSet.add(FULL_RANGE);
+ delsRangeSet.add(FULL_RANGE);
+ schema.indexes.forEach(function (idx) { return getRangeSet(idx.name).add(FULL_RANGE); });
+ }
+ return table.mutate(req).then(function (res) {
+ if (keys && (req.type === 'add' || req.type === 'put')) {
+ pkRangeSet.addKeys(res.results);
+ if (indexesWithAutoIncPK) {
+ indexesWithAutoIncPK.forEach(function (idx) {
+ var idxVals = req.values.map(function (v) { return idx.extractKey(v); });
+ var pkPos = idx.keyPath.findIndex(function (prop) { return prop === primaryKey.keyPath; });
+ for (var i = 0, len = res.results.length; i < len; ++i) {
+ idxVals[i][pkPos] = res.results[i];
+ }
+ getRangeSet(idx.name).addKeys(idxVals);
+ });
+ }
+ }
+ trans.mutatedParts = extendObservabilitySet(trans.mutatedParts || {}, mutatedParts);
+ return res;
+ });
+ } });
+ var getRange = function (_a) {
+ var _b, _c;
+ var _d = _a.query, index = _d.index, range = _d.range;
+ return [
+ index,
+ new RangeSet((_b = range.lower) !== null && _b !== void 0 ? _b : core.MIN_KEY, (_c = range.upper) !== null && _c !== void 0 ? _c : core.MAX_KEY),
+ ];
+ };
+ var readSubscribers = {
+ get: function (req) { return [primaryKey, new RangeSet(req.key)]; },
+ getMany: function (req) { return [primaryKey, new RangeSet().addKeys(req.keys)]; },
+ count: getRange,
+ query: getRange,
+ openCursor: getRange,
+ };
+ keys(readSubscribers).forEach(function (method) {
+ tableClone[method] = function (req) {
+ var subscr = PSD.subscr;
+ var isLiveQuery = !!subscr;
+ var cachable = isCachableContext(PSD, table) && isCachableRequest(method, req);
+ var obsSet = cachable
+ ? req.obsSet = {}
+ : subscr;
+ if (isLiveQuery) {
+ var getRangeSet = function (indexName) {
+ var part = "idb://".concat(dbName, "/").concat(tableName, "/").concat(indexName);
+ return (obsSet[part] ||
+ (obsSet[part] = new RangeSet()));
+ };
+ var pkRangeSet_1 = getRangeSet("");
+ var delsRangeSet_1 = getRangeSet(":dels");
+ var _a = readSubscribers[method](req), queriedIndex = _a[0], queriedRanges = _a[1];
+ if (method === 'query' && queriedIndex.isPrimaryKey && !req.values) {
+ delsRangeSet_1.add(queriedRanges);
+ }
+ else {
+ getRangeSet(queriedIndex.name || "").add(queriedRanges);
+ }
+ if (!queriedIndex.isPrimaryKey) {
+ if (method === "count") {
+ delsRangeSet_1.add(FULL_RANGE);
+ }
+ else {
+ var keysPromise_1 = method === "query" &&
+ outbound &&
+ req.values &&
+ table.query(__assign(__assign({}, req), { values: false }));
+ return table[method].apply(this, arguments).then(function (res) {
+ if (method === "query") {
+ if (outbound && req.values) {
+ return keysPromise_1.then(function (_a) {
+ var resultingKeys = _a.result;
+ pkRangeSet_1.addKeys(resultingKeys);
+ return res;
+ });
+ }
+ var pKeys = req.values
+ ? res.result.map(extractKey)
+ : res.result;
+ if (req.values) {
+ pkRangeSet_1.addKeys(pKeys);
+ }
+ else {
+ delsRangeSet_1.addKeys(pKeys);
+ }
+ }
+ else if (method === "openCursor") {
+ var cursor_1 = res;
+ var wantValues_1 = req.values;
+ return (cursor_1 &&
+ Object.create(cursor_1, {
+ key: {
+ get: function () {
+ delsRangeSet_1.addKey(cursor_1.primaryKey);
+ return cursor_1.key;
+ },
+ },
+ primaryKey: {
+ get: function () {
+ var pkey = cursor_1.primaryKey;
+ delsRangeSet_1.addKey(pkey);
+ return pkey;
+ },
+ },
+ value: {
+ get: function () {
+ wantValues_1 && pkRangeSet_1.addKey(cursor_1.primaryKey);
+ return cursor_1.value;
+ },
+ },
+ }));
+ }
+ return res;
+ });
+ }
+ }
+ }
+ return table[method].apply(this, arguments);
+ };
+ });
+ return tableClone;
+ } });
+ },
+ };
+ function trackAffectedIndexes(getRangeSet, schema, oldObjs, newObjs) {
+ function addAffectedIndex(ix) {
+ var rangeSet = getRangeSet(ix.name || "");
+ function extractKey(obj) {
+ return obj != null ? ix.extractKey(obj) : null;
+ }
+ var addKeyOrKeys = function (key) { return ix.multiEntry && isArray(key)
+ ? key.forEach(function (key) { return rangeSet.addKey(key); })
+ : rangeSet.addKey(key); };
+ (oldObjs || newObjs).forEach(function (_, i) {
+ var oldKey = oldObjs && extractKey(oldObjs[i]);
+ var newKey = newObjs && extractKey(newObjs[i]);
+ if (cmp(oldKey, newKey) !== 0) {
+ if (oldKey != null)
+ addKeyOrKeys(oldKey);
+ if (newKey != null)
+ addKeyOrKeys(newKey);
+ }
+ });
+ }
+ schema.indexes.forEach(addAffectedIndex);
+ }
+
+ function adjustOptimisticFromFailures(tblCache, req, res) {
+ if (res.numFailures === 0)
+ return req;
+ if (req.type === 'deleteRange') {
+ return null;
+ }
+ var numBulkOps = req.keys
+ ? req.keys.length
+ : 'values' in req && req.values
+ ? req.values.length
+ : 1;
+ if (res.numFailures === numBulkOps) {
+ return null;
+ }
+ var clone = __assign({}, req);
+ if (isArray(clone.keys)) {
+ clone.keys = clone.keys.filter(function (_, i) { return !(i in res.failures); });
+ }
+ if ('values' in clone && isArray(clone.values)) {
+ clone.values = clone.values.filter(function (_, i) { return !(i in res.failures); });
+ }
+ return clone;
+ }
+
+ function isAboveLower(key, range) {
+ return range.lower === undefined
+ ? true
+ : range.lowerOpen
+ ? cmp(key, range.lower) > 0
+ : cmp(key, range.lower) >= 0;
+ }
+ function isBelowUpper(key, range) {
+ return range.upper === undefined
+ ? true
+ : range.upperOpen
+ ? cmp(key, range.upper) < 0
+ : cmp(key, range.upper) <= 0;
+ }
+ function isWithinRange(key, range) {
+ return isAboveLower(key, range) && isBelowUpper(key, range);
+ }
+
+ function applyOptimisticOps(result, req, ops, table, cacheEntry, immutable) {
+ if (!ops || ops.length === 0)
+ return result;
+ var index = req.query.index;
+ var multiEntry = index.multiEntry;
+ var queryRange = req.query.range;
+ var primaryKey = table.schema.primaryKey;
+ var extractPrimKey = primaryKey.extractKey;
+ var extractIndex = index.extractKey;
+ var extractLowLevelIndex = (index.lowLevelIndex || index).extractKey;
+ var finalResult = ops.reduce(function (result, op) {
+ var modifedResult = result;
+ var includedValues = [];
+ if (op.type === 'add' || op.type === 'put') {
+ var includedPKs = new RangeSet();
+ for (var i = op.values.length - 1; i >= 0; --i) {
+ var value = op.values[i];
+ var pk = extractPrimKey(value);
+ if (includedPKs.hasKey(pk))
+ continue;
+ var key = extractIndex(value);
+ if (multiEntry && isArray(key)
+ ? key.some(function (k) { return isWithinRange(k, queryRange); })
+ : isWithinRange(key, queryRange)) {
+ includedPKs.addKey(pk);
+ includedValues.push(value);
+ }
+ }
+ }
+ switch (op.type) {
+ case 'add': {
+ var existingKeys_1 = new RangeSet().addKeys(req.values ? result.map(function (v) { return extractPrimKey(v); }) : result);
+ modifedResult = result.concat(req.values
+ ? includedValues.filter(function (v) {
+ var key = extractPrimKey(v);
+ if (existingKeys_1.hasKey(key))
+ return false;
+ existingKeys_1.addKey(key);
+ return true;
+ })
+ : includedValues
+ .map(function (v) { return extractPrimKey(v); })
+ .filter(function (k) {
+ if (existingKeys_1.hasKey(k))
+ return false;
+ existingKeys_1.addKey(k);
+ return true;
+ }));
+ break;
+ }
+ case 'put': {
+ var keySet_1 = new RangeSet().addKeys(op.values.map(function (v) { return extractPrimKey(v); }));
+ modifedResult = result
+ .filter(
+ function (item) { return !keySet_1.hasKey(req.values ? extractPrimKey(item) : item); })
+ .concat(
+ req.values
+ ? includedValues
+ : includedValues.map(function (v) { return extractPrimKey(v); }));
+ break;
+ }
+ case 'delete':
+ var keysToDelete_1 = new RangeSet().addKeys(op.keys);
+ modifedResult = result.filter(function (item) {
+ return !keysToDelete_1.hasKey(req.values ? extractPrimKey(item) : item);
+ });
+ break;
+ case 'deleteRange':
+ var range_1 = op.range;
+ modifedResult = result.filter(function (item) { return !isWithinRange(extractPrimKey(item), range_1); });
+ break;
+ }
+ return modifedResult;
+ }, result);
+ if (finalResult === result)
+ return result;
+ finalResult.sort(function (a, b) {
+ return cmp(extractLowLevelIndex(a), extractLowLevelIndex(b)) ||
+ cmp(extractPrimKey(a), extractPrimKey(b));
+ });
+ if (req.limit && req.limit < Infinity) {
+ if (finalResult.length > req.limit) {
+ finalResult.length = req.limit;
+ }
+ else if (result.length === req.limit && finalResult.length < req.limit) {
+ cacheEntry.dirty = true;
+ }
+ }
+ return immutable ? Object.freeze(finalResult) : finalResult;
+ }
+
+ function areRangesEqual(r1, r2) {
+ return (cmp(r1.lower, r2.lower) === 0 &&
+ cmp(r1.upper, r2.upper) === 0 &&
+ !!r1.lowerOpen === !!r2.lowerOpen &&
+ !!r1.upperOpen === !!r2.upperOpen);
+ }
+
+ function compareLowers(lower1, lower2, lowerOpen1, lowerOpen2) {
+ if (lower1 === undefined)
+ return lower2 !== undefined ? -1 : 0;
+ if (lower2 === undefined)
+ return 1;
+ var c = cmp(lower1, lower2);
+ if (c === 0) {
+ if (lowerOpen1 && lowerOpen2)
+ return 0;
+ if (lowerOpen1)
+ return 1;
+ if (lowerOpen2)
+ return -1;
+ }
+ return c;
+ }
+ function compareUppers(upper1, upper2, upperOpen1, upperOpen2) {
+ if (upper1 === undefined)
+ return upper2 !== undefined ? 1 : 0;
+ if (upper2 === undefined)
+ return -1;
+ var c = cmp(upper1, upper2);
+ if (c === 0) {
+ if (upperOpen1 && upperOpen2)
+ return 0;
+ if (upperOpen1)
+ return -1;
+ if (upperOpen2)
+ return 1;
+ }
+ return c;
+ }
+ function isSuperRange(r1, r2) {
+ return (compareLowers(r1.lower, r2.lower, r1.lowerOpen, r2.lowerOpen) <= 0 &&
+ compareUppers(r1.upper, r2.upper, r1.upperOpen, r2.upperOpen) >= 0);
+ }
+
+ function findCompatibleQuery(dbName, tableName, type, req) {
+ var tblCache = cache["idb://".concat(dbName, "/").concat(tableName)];
+ if (!tblCache)
+ return [];
+ var queries = tblCache.queries[type];
+ if (!queries)
+ return [null, false, tblCache, null];
+ var indexName = req.query ? req.query.index.name : null;
+ var entries = queries[indexName || ''];
+ if (!entries)
+ return [null, false, tblCache, null];
+ switch (type) {
+ case 'query':
+ var equalEntry = entries.find(function (entry) {
+ return entry.req.limit === req.limit &&
+ entry.req.values === req.values &&
+ areRangesEqual(entry.req.query.range, req.query.range);
+ });
+ if (equalEntry)
+ return [
+ equalEntry,
+ true,
+ tblCache,
+ entries,
+ ];
+ var superEntry = entries.find(function (entry) {
+ var limit = 'limit' in entry.req ? entry.req.limit : Infinity;
+ return (limit >= req.limit &&
+ (req.values ? entry.req.values : true) &&
+ isSuperRange(entry.req.query.range, req.query.range));
+ });
+ return [superEntry, false, tblCache, entries];
+ case 'count':
+ var countQuery = entries.find(function (entry) {
+ return areRangesEqual(entry.req.query.range, req.query.range);
+ });
+ return [countQuery, !!countQuery, tblCache, entries];
+ }
+ }
+
+ function subscribeToCacheEntry(cacheEntry, container, requery, signal) {
+ cacheEntry.subscribers.add(requery);
+ signal.addEventListener("abort", function () {
+ cacheEntry.subscribers.delete(requery);
+ if (cacheEntry.subscribers.size === 0) {
+ enqueForDeletion(cacheEntry, container);
+ }
+ });
+ }
+ function enqueForDeletion(cacheEntry, container) {
+ setTimeout(function () {
+ if (cacheEntry.subscribers.size === 0) {
+ delArrayItem(container, cacheEntry);
+ }
+ }, 3000);
+ }
+
+ var cacheMiddleware = {
+ stack: 'dbcore',
+ level: 0,
+ name: 'Cache',
+ create: function (core) {
+ var dbName = core.schema.name;
+ var coreMW = __assign(__assign({}, core), { transaction: function (stores, mode, options) {
+ var idbtrans = core.transaction(stores, mode, options);
+ if (mode === 'readwrite') {
+ var ac_1 = new AbortController();
+ var signal = ac_1.signal;
+ var endTransaction = function (wasCommitted) { return function () {
+ ac_1.abort();
+ if (mode === 'readwrite') {
+ var affectedSubscribers_1 = new Set();
+ for (var _i = 0, stores_1 = stores; _i < stores_1.length; _i++) {
+ var storeName = stores_1[_i];
+ var tblCache = cache["idb://".concat(dbName, "/").concat(storeName)];
+ if (tblCache) {
+ var table = core.table(storeName);
+ var ops = tblCache.optimisticOps.filter(function (op) { return op.trans === idbtrans; });
+ if (idbtrans._explicit && wasCommitted && idbtrans.mutatedParts) {
+ for (var _a = 0, _b = Object.values(tblCache.queries.query); _a < _b.length; _a++) {
+ var entries = _b[_a];
+ for (var _c = 0, _d = entries.slice(); _c < _d.length; _c++) {
+ var entry = _d[_c];
+ if (obsSetsOverlap(entry.obsSet, idbtrans.mutatedParts)) {
+ delArrayItem(entries, entry);
+ entry.subscribers.forEach(function (requery) { return affectedSubscribers_1.add(requery); });
+ }
+ }
+ }
+ }
+ else if (ops.length > 0) {
+ tblCache.optimisticOps = tblCache.optimisticOps.filter(function (op) { return op.trans !== idbtrans; });
+ for (var _e = 0, _f = Object.values(tblCache.queries.query); _e < _f.length; _e++) {
+ var entries = _f[_e];
+ for (var _g = 0, _h = entries.slice(); _g < _h.length; _g++) {
+ var entry = _h[_g];
+ if (entry.res != null &&
+ idbtrans.mutatedParts
+ ) {
+ if (wasCommitted && !entry.dirty) {
+ var freezeResults = Object.isFrozen(entry.res);
+ var modRes = applyOptimisticOps(entry.res, entry.req, ops, table, entry, freezeResults);
+ if (entry.dirty) {
+ delArrayItem(entries, entry);
+ entry.subscribers.forEach(function (requery) { return affectedSubscribers_1.add(requery); });
+ }
+ else if (modRes !== entry.res) {
+ entry.res = modRes;
+ entry.promise = DexiePromise.resolve({ result: modRes });
+ }
+ }
+ else {
+ if (entry.dirty) {
+ delArrayItem(entries, entry);
+ }
+ entry.subscribers.forEach(function (requery) { return affectedSubscribers_1.add(requery); });
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ affectedSubscribers_1.forEach(function (requery) { return requery(); });
+ }
+ }; };
+ idbtrans.addEventListener('abort', endTransaction(false), {
+ signal: signal,
+ });
+ idbtrans.addEventListener('error', endTransaction(false), {
+ signal: signal,
+ });
+ idbtrans.addEventListener('complete', endTransaction(true), {
+ signal: signal,
+ });
+ }
+ return idbtrans;
+ }, table: function (tableName) {
+ var downTable = core.table(tableName);
+ var primKey = downTable.schema.primaryKey;
+ var tableMW = __assign(__assign({}, downTable), { mutate: function (req) {
+ var trans = PSD.trans;
+ if (primKey.outbound ||
+ trans.db._options.cache === 'disabled' ||
+ trans.explicit ||
+ trans.idbtrans.mode !== 'readwrite'
+ ) {
+ return downTable.mutate(req);
+ }
+ var tblCache = cache["idb://".concat(dbName, "/").concat(tableName)];
+ if (!tblCache)
+ return downTable.mutate(req);
+ var promise = downTable.mutate(req);
+ if ((req.type === 'add' || req.type === 'put') && (req.values.length >= 50 || getEffectiveKeys(primKey, req).some(function (key) { return key == null; }))) {
+ promise.then(function (res) {
+ var reqWithResolvedKeys = __assign(__assign({}, req), { values: req.values.map(function (value, i) {
+ var _a;
+ if (res.failures[i])
+ return value;
+ var valueWithKey = ((_a = primKey.keyPath) === null || _a === void 0 ? void 0 : _a.includes('.'))
+ ? deepClone(value)
+ : __assign({}, value);
+ setByKeyPath(valueWithKey, primKey.keyPath, res.results[i]);
+ return valueWithKey;
+ }) });
+ var adjustedReq = adjustOptimisticFromFailures(tblCache, reqWithResolvedKeys, res);
+ tblCache.optimisticOps.push(adjustedReq);
+ queueMicrotask(function () { return req.mutatedParts && signalSubscribersLazily(req.mutatedParts); });
+ });
+ }
+ else {
+ tblCache.optimisticOps.push(req);
+ req.mutatedParts && signalSubscribersLazily(req.mutatedParts);
+ promise.then(function (res) {
+ if (res.numFailures > 0) {
+ delArrayItem(tblCache.optimisticOps, req);
+ var adjustedReq = adjustOptimisticFromFailures(tblCache, req, res);
+ if (adjustedReq) {
+ tblCache.optimisticOps.push(adjustedReq);
+ }
+ req.mutatedParts && signalSubscribersLazily(req.mutatedParts);
+ }
+ });
+ promise.catch(function () {
+ delArrayItem(tblCache.optimisticOps, req);
+ req.mutatedParts && signalSubscribersLazily(req.mutatedParts);
+ });
+ }
+ return promise;
+ }, query: function (req) {
+ var _a;
+ if (!isCachableContext(PSD, downTable) || !isCachableRequest("query", req))
+ return downTable.query(req);
+ var freezeResults = ((_a = PSD.trans) === null || _a === void 0 ? void 0 : _a.db._options.cache) === 'immutable';
+ var _b = PSD, requery = _b.requery, signal = _b.signal;
+ var _c = findCompatibleQuery(dbName, tableName, 'query', req), cacheEntry = _c[0], exactMatch = _c[1], tblCache = _c[2], container = _c[3];
+ if (cacheEntry && exactMatch) {
+ cacheEntry.obsSet = req.obsSet;
+ }
+ else {
+ var promise = downTable.query(req).then(function (res) {
+ var result = res.result;
+ if (cacheEntry)
+ cacheEntry.res = result;
+ if (freezeResults) {
+ for (var i = 0, l = result.length; i < l; ++i) {
+ Object.freeze(result[i]);
+ }
+ Object.freeze(result);
+ }
+ else {
+ res.result = deepClone(result);
+ }
+ return res;
+ }).catch(function (error) {
+ if (container && cacheEntry)
+ delArrayItem(container, cacheEntry);
+ return Promise.reject(error);
+ });
+ cacheEntry = {
+ obsSet: req.obsSet,
+ promise: promise,
+ subscribers: new Set(),
+ type: 'query',
+ req: req,
+ dirty: false,
+ };
+ if (container) {
+ container.push(cacheEntry);
+ }
+ else {
+ container = [cacheEntry];
+ if (!tblCache) {
+ tblCache = cache["idb://".concat(dbName, "/").concat(tableName)] = {
+ queries: {
+ query: {},
+ count: {},
+ },
+ objs: new Map(),
+ optimisticOps: [],
+ unsignaledParts: {}
+ };
+ }
+ tblCache.queries.query[req.query.index.name || ''] = container;
+ }
+ }
+ subscribeToCacheEntry(cacheEntry, container, requery, signal);
+ return cacheEntry.promise.then(function (res) {
+ return {
+ result: applyOptimisticOps(res.result, req, tblCache === null || tblCache === void 0 ? void 0 : tblCache.optimisticOps, downTable, cacheEntry, freezeResults),
+ };
+ });
+ } });
+ return tableMW;
+ } });
+ return coreMW;
+ },
+ };
+
+ function vipify(target, vipDb) {
+ return new Proxy(target, {
+ get: function (target, prop, receiver) {
+ if (prop === 'db')
+ return vipDb;
+ return Reflect.get(target, prop, receiver);
+ }
+ });
+ }
+
+ var Dexie$1 = (function () {
+ function Dexie(name, options) {
+ var _this = this;
+ this._middlewares = {};
+ this.verno = 0;
+ var deps = Dexie.dependencies;
+ this._options = options = __assign({
+ addons: Dexie.addons, autoOpen: true,
+ indexedDB: deps.indexedDB, IDBKeyRange: deps.IDBKeyRange, cache: 'cloned' }, options);
+ this._deps = {
+ indexedDB: options.indexedDB,
+ IDBKeyRange: options.IDBKeyRange
+ };
+ var addons = options.addons;
+ this._dbSchema = {};
+ this._versions = [];
+ this._storeNames = [];
+ this._allTables = {};
+ this.idbdb = null;
+ this._novip = this;
+ var state = {
+ dbOpenError: null,
+ isBeingOpened: false,
+ onReadyBeingFired: null,
+ openComplete: false,
+ dbReadyResolve: nop,
+ dbReadyPromise: null,
+ cancelOpen: nop,
+ openCanceller: null,
+ autoSchema: true,
+ PR1398_maxLoop: 3,
+ autoOpen: options.autoOpen,
+ };
+ state.dbReadyPromise = new DexiePromise(function (resolve) {
+ state.dbReadyResolve = resolve;
+ });
+ state.openCanceller = new DexiePromise(function (_, reject) {
+ state.cancelOpen = reject;
+ });
+ this._state = state;
+ this.name = name;
+ this.on = Events(this, "populate", "blocked", "versionchange", "close", { ready: [promisableChain, nop] });
+ this.once = function (event, callback) {
+ var fn = function () {
+ var args = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ args[_i] = arguments[_i];
+ }
+ _this.on(event).unsubscribe(fn);
+ callback.apply(_this, args);
+ };
+ return _this.on(event, fn);
+ };
+ this.on.ready.subscribe = override(this.on.ready.subscribe, function (subscribe) {
+ return function (subscriber, bSticky) {
+ Dexie.vip(function () {
+ var state = _this._state;
+ if (state.openComplete) {
+ if (!state.dbOpenError)
+ DexiePromise.resolve().then(subscriber);
+ if (bSticky)
+ subscribe(subscriber);
+ }
+ else if (state.onReadyBeingFired) {
+ state.onReadyBeingFired.push(subscriber);
+ if (bSticky)
+ subscribe(subscriber);
+ }
+ else {
+ subscribe(subscriber);
+ var db_1 = _this;
+ if (!bSticky)
+ subscribe(function unsubscribe() {
+ db_1.on.ready.unsubscribe(subscriber);
+ db_1.on.ready.unsubscribe(unsubscribe);
+ });
+ }
+ });
+ };
+ });
+ this.Collection = createCollectionConstructor(this);
+ this.Table = createTableConstructor(this);
+ this.Transaction = createTransactionConstructor(this);
+ this.Version = createVersionConstructor(this);
+ this.WhereClause = createWhereClauseConstructor(this);
+ this.on("versionchange", function (ev) {
+ if (ev.newVersion > 0)
+ console.warn("Another connection wants to upgrade database '".concat(_this.name, "'. Closing db now to resume the upgrade."));
+ else
+ console.warn("Another connection wants to delete database '".concat(_this.name, "'. Closing db now to resume the delete request."));
+ _this.close({ disableAutoOpen: false });
+ });
+ this.on("blocked", function (ev) {
+ if (!ev.newVersion || ev.newVersion < ev.oldVersion)
+ console.warn("Dexie.delete('".concat(_this.name, "') was blocked"));
+ else
+ console.warn("Upgrade '".concat(_this.name, "' blocked by other connection holding version ").concat(ev.oldVersion / 10));
+ });
+ this._maxKey = getMaxKey(options.IDBKeyRange);
+ this._createTransaction = function (mode, storeNames, dbschema, parentTransaction) { return new _this.Transaction(mode, storeNames, dbschema, _this._options.chromeTransactionDurability, parentTransaction); };
+ this._fireOnBlocked = function (ev) {
+ _this.on("blocked").fire(ev);
+ connections
+ .filter(function (c) { return c.name === _this.name && c !== _this && !c._state.vcFired; })
+ .map(function (c) { return c.on("versionchange").fire(ev); });
+ };
+ this.use(cacheExistingValuesMiddleware);
+ this.use(cacheMiddleware);
+ this.use(observabilityMiddleware);
+ this.use(virtualIndexMiddleware);
+ this.use(hooksMiddleware);
+ var vipDB = new Proxy(this, {
+ get: function (_, prop, receiver) {
+ if (prop === '_vip')
+ return true;
+ if (prop === 'table')
+ return function (tableName) { return vipify(_this.table(tableName), vipDB); };
+ var rv = Reflect.get(_, prop, receiver);
+ if (rv instanceof Table)
+ return vipify(rv, vipDB);
+ if (prop === 'tables')
+ return rv.map(function (t) { return vipify(t, vipDB); });
+ if (prop === '_createTransaction')
+ return function () {
+ var tx = rv.apply(this, arguments);
+ return vipify(tx, vipDB);
+ };
+ return rv;
+ }
+ });
+ this.vip = vipDB;
+ addons.forEach(function (addon) { return addon(_this); });
+ }
+ Dexie.prototype.version = function (versionNumber) {
+ if (isNaN(versionNumber) || versionNumber < 0.1)
+ throw new exceptions.Type("Given version is not a positive number");
+ versionNumber = Math.round(versionNumber * 10) / 10;
+ if (this.idbdb || this._state.isBeingOpened)
+ throw new exceptions.Schema("Cannot add version when database is open");
+ this.verno = Math.max(this.verno, versionNumber);
+ var versions = this._versions;
+ var versionInstance = versions.filter(function (v) { return v._cfg.version === versionNumber; })[0];
+ if (versionInstance)
+ return versionInstance;
+ versionInstance = new this.Version(versionNumber);
+ versions.push(versionInstance);
+ versions.sort(lowerVersionFirst);
+ versionInstance.stores({});
+ this._state.autoSchema = false;
+ return versionInstance;
+ };
+ Dexie.prototype._whenReady = function (fn) {
+ var _this = this;
+ return (this.idbdb && (this._state.openComplete || PSD.letThrough || this._vip)) ? fn() : new DexiePromise(function (resolve, reject) {
+ if (_this._state.openComplete) {
+ return reject(new exceptions.DatabaseClosed(_this._state.dbOpenError));
+ }
+ if (!_this._state.isBeingOpened) {
+ if (!_this._state.autoOpen) {
+ reject(new exceptions.DatabaseClosed());
+ return;
+ }
+ _this.open().catch(nop);
+ }
+ _this._state.dbReadyPromise.then(resolve, reject);
+ }).then(fn);
+ };
+ Dexie.prototype.use = function (_a) {
+ var stack = _a.stack, create = _a.create, level = _a.level, name = _a.name;
+ if (name)
+ this.unuse({ stack: stack, name: name });
+ var middlewares = this._middlewares[stack] || (this._middlewares[stack] = []);
+ middlewares.push({ stack: stack, create: create, level: level == null ? 10 : level, name: name });
+ middlewares.sort(function (a, b) { return a.level - b.level; });
+ return this;
+ };
+ Dexie.prototype.unuse = function (_a) {
+ var stack = _a.stack, name = _a.name, create = _a.create;
+ if (stack && this._middlewares[stack]) {
+ this._middlewares[stack] = this._middlewares[stack].filter(function (mw) {
+ return create ? mw.create !== create :
+ name ? mw.name !== name :
+ false;
+ });
+ }
+ return this;
+ };
+ Dexie.prototype.open = function () {
+ var _this = this;
+ return usePSD(globalPSD,
+ function () { return dexieOpen(_this); });
+ };
+ Dexie.prototype._close = function () {
+ this.on.close.fire(new CustomEvent('close'));
+ var state = this._state;
+ var idx = connections.indexOf(this);
+ if (idx >= 0)
+ connections.splice(idx, 1);
+ if (this.idbdb) {
+ try {
+ this.idbdb.close();
+ }
+ catch (e) { }
+ this.idbdb = null;
+ }
+ if (!state.isBeingOpened) {
+ state.dbReadyPromise = new DexiePromise(function (resolve) {
+ state.dbReadyResolve = resolve;
+ });
+ state.openCanceller = new DexiePromise(function (_, reject) {
+ state.cancelOpen = reject;
+ });
+ }
+ };
+ Dexie.prototype.close = function (_a) {
+ var _b = _a === void 0 ? { disableAutoOpen: true } : _a, disableAutoOpen = _b.disableAutoOpen;
+ var state = this._state;
+ if (disableAutoOpen) {
+ if (state.isBeingOpened) {
+ state.cancelOpen(new exceptions.DatabaseClosed());
+ }
+ this._close();
+ state.autoOpen = false;
+ state.dbOpenError = new exceptions.DatabaseClosed();
+ }
+ else {
+ this._close();
+ state.autoOpen = this._options.autoOpen ||
+ state.isBeingOpened;
+ state.openComplete = false;
+ state.dbOpenError = null;
+ }
+ };
+ Dexie.prototype.delete = function (closeOptions) {
+ var _this = this;
+ if (closeOptions === void 0) { closeOptions = { disableAutoOpen: true }; }
+ var hasInvalidArguments = arguments.length > 0 && typeof arguments[0] !== 'object';
+ var state = this._state;
+ return new DexiePromise(function (resolve, reject) {
+ var doDelete = function () {
+ _this.close(closeOptions);
+ var req = _this._deps.indexedDB.deleteDatabase(_this.name);
+ req.onsuccess = wrap(function () {
+ _onDatabaseDeleted(_this._deps, _this.name);
+ resolve();
+ });
+ req.onerror = eventRejectHandler(reject);
+ req.onblocked = _this._fireOnBlocked;
+ };
+ if (hasInvalidArguments)
+ throw new exceptions.InvalidArgument("Invalid closeOptions argument to db.delete()");
+ if (state.isBeingOpened) {
+ state.dbReadyPromise.then(doDelete);
+ }
+ else {
+ doDelete();
+ }
+ });
+ };
+ Dexie.prototype.backendDB = function () {
+ return this.idbdb;
+ };
+ Dexie.prototype.isOpen = function () {
+ return this.idbdb !== null;
+ };
+ Dexie.prototype.hasBeenClosed = function () {
+ var dbOpenError = this._state.dbOpenError;
+ return dbOpenError && (dbOpenError.name === 'DatabaseClosed');
+ };
+ Dexie.prototype.hasFailed = function () {
+ return this._state.dbOpenError !== null;
+ };
+ Dexie.prototype.dynamicallyOpened = function () {
+ return this._state.autoSchema;
+ };
+ Object.defineProperty(Dexie.prototype, "tables", {
+ get: function () {
+ var _this = this;
+ return keys(this._allTables).map(function (name) { return _this._allTables[name]; });
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Dexie.prototype.transaction = function () {
+ var args = extractTransactionArgs.apply(this, arguments);
+ return this._transaction.apply(this, args);
+ };
+ Dexie.prototype._transaction = function (mode, tables, scopeFunc) {
+ var _this = this;
+ var parentTransaction = PSD.trans;
+ if (!parentTransaction || parentTransaction.db !== this || mode.indexOf('!') !== -1)
+ parentTransaction = null;
+ var onlyIfCompatible = mode.indexOf('?') !== -1;
+ mode = mode.replace('!', '').replace('?', '');
+ var idbMode, storeNames;
+ try {
+ storeNames = tables.map(function (table) {
+ var storeName = table instanceof _this.Table ? table.name : table;
+ if (typeof storeName !== 'string')
+ throw new TypeError("Invalid table argument to Dexie.transaction(). Only Table or String are allowed");
+ return storeName;
+ });
+ if (mode == "r" || mode === READONLY)
+ idbMode = READONLY;
+ else if (mode == "rw" || mode == READWRITE)
+ idbMode = READWRITE;
+ else
+ throw new exceptions.InvalidArgument("Invalid transaction mode: " + mode);
+ if (parentTransaction) {
+ if (parentTransaction.mode === READONLY && idbMode === READWRITE) {
+ if (onlyIfCompatible) {
+ parentTransaction = null;
+ }
+ else
+ throw new exceptions.SubTransaction("Cannot enter a sub-transaction with READWRITE mode when parent transaction is READONLY");
+ }
+ if (parentTransaction) {
+ storeNames.forEach(function (storeName) {
+ if (parentTransaction && parentTransaction.storeNames.indexOf(storeName) === -1) {
+ if (onlyIfCompatible) {
+ parentTransaction = null;
+ }
+ else
+ throw new exceptions.SubTransaction("Table " + storeName +
+ " not included in parent transaction.");
+ }
+ });
+ }
+ if (onlyIfCompatible && parentTransaction && !parentTransaction.active) {
+ parentTransaction = null;
+ }
+ }
+ }
+ catch (e) {
+ return parentTransaction ?
+ parentTransaction._promise(null, function (_, reject) { reject(e); }) :
+ rejection(e);
+ }
+ var enterTransaction = enterTransactionScope.bind(null, this, idbMode, storeNames, parentTransaction, scopeFunc);
+ return (parentTransaction ?
+ parentTransaction._promise(idbMode, enterTransaction, "lock") :
+ PSD.trans ?
+ usePSD(PSD.transless, function () { return _this._whenReady(enterTransaction); }) :
+ this._whenReady(enterTransaction));
+ };
+ Dexie.prototype.table = function (tableName) {
+ if (!hasOwn(this._allTables, tableName)) {
+ throw new exceptions.InvalidTable("Table ".concat(tableName, " does not exist"));
+ }
+ return this._allTables[tableName];
+ };
+ return Dexie;
+ }());
+
+ var symbolObservable = typeof Symbol !== "undefined" && "observable" in Symbol
+ ? Symbol.observable
+ : "@@observable";
+ var Observable = (function () {
+ function Observable(subscribe) {
+ this._subscribe = subscribe;
+ }
+ Observable.prototype.subscribe = function (x, error, complete) {
+ return this._subscribe(!x || typeof x === "function" ? { next: x, error: error, complete: complete } : x);
+ };
+ Observable.prototype[symbolObservable] = function () {
+ return this;
+ };
+ return Observable;
+ }());
+
+ var domDeps;
+ try {
+ domDeps = {
+ indexedDB: _global.indexedDB || _global.mozIndexedDB || _global.webkitIndexedDB || _global.msIndexedDB,
+ IDBKeyRange: _global.IDBKeyRange || _global.webkitIDBKeyRange
+ };
+ }
+ catch (e) {
+ domDeps = { indexedDB: null, IDBKeyRange: null };
+ }
+
+ function liveQuery(querier) {
+ var hasValue = false;
+ var currentValue;
+ var observable = new Observable(function (observer) {
+ var scopeFuncIsAsync = isAsyncFunction(querier);
+ function execute(ctx) {
+ var wasRootExec = beginMicroTickScope();
+ try {
+ if (scopeFuncIsAsync) {
+ incrementExpectedAwaits();
+ }
+ var rv = newScope(querier, ctx);
+ if (scopeFuncIsAsync) {
+ rv = rv.finally(decrementExpectedAwaits);
+ }
+ return rv;
+ }
+ finally {
+ wasRootExec && endMicroTickScope();
+ }
+ }
+ var closed = false;
+ var abortController;
+ var accumMuts = {};
+ var currentObs = {};
+ var subscription = {
+ get closed() {
+ return closed;
+ },
+ unsubscribe: function () {
+ if (closed)
+ return;
+ closed = true;
+ if (abortController)
+ abortController.abort();
+ if (startedListening)
+ globalEvents.storagemutated.unsubscribe(mutationListener);
+ },
+ };
+ observer.start && observer.start(subscription);
+ var startedListening = false;
+ var doQuery = function () { return execInGlobalContext(_doQuery); };
+ function shouldNotify() {
+ return obsSetsOverlap(currentObs, accumMuts);
+ }
+ var mutationListener = function (parts) {
+ extendObservabilitySet(accumMuts, parts);
+ if (shouldNotify()) {
+ doQuery();
+ }
+ };
+ var _doQuery = function () {
+ if (closed ||
+ !domDeps.indexedDB)
+ {
+ return;
+ }
+ accumMuts = {};
+ var subscr = {};
+ if (abortController)
+ abortController.abort();
+ abortController = new AbortController();
+ var ctx = {
+ subscr: subscr,
+ signal: abortController.signal,
+ requery: doQuery,
+ querier: querier,
+ trans: null
+ };
+ var ret = execute(ctx);
+ Promise.resolve(ret).then(function (result) {
+ hasValue = true;
+ currentValue = result;
+ if (closed || ctx.signal.aborted) {
+ return;
+ }
+ accumMuts = {};
+ currentObs = subscr;
+ if (!objectIsEmpty(currentObs) && !startedListening) {
+ globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, mutationListener);
+ startedListening = true;
+ }
+ execInGlobalContext(function () { return !closed && observer.next && observer.next(result); });
+ }, function (err) {
+ hasValue = false;
+ if (!['DatabaseClosedError', 'AbortError'].includes(err === null || err === void 0 ? void 0 : err.name)) {
+ if (!closed)
+ execInGlobalContext(function () {
+ if (closed)
+ return;
+ observer.error && observer.error(err);
+ });
+ }
+ });
+ };
+ setTimeout(doQuery, 0);
+ return subscription;
+ });
+ observable.hasValue = function () { return hasValue; };
+ observable.getValue = function () { return currentValue; };
+ return observable;
+ }
+
+ var Dexie = Dexie$1;
+ props(Dexie, __assign(__assign({}, fullNameExceptions), {
+ delete: function (databaseName) {
+ var db = new Dexie(databaseName, { addons: [] });
+ return db.delete();
+ },
+ exists: function (name) {
+ return new Dexie(name, { addons: [] }).open().then(function (db) {
+ db.close();
+ return true;
+ }).catch('NoSuchDatabaseError', function () { return false; });
+ },
+ getDatabaseNames: function (cb) {
+ try {
+ return getDatabaseNames(Dexie.dependencies).then(cb);
+ }
+ catch (_a) {
+ return rejection(new exceptions.MissingAPI());
+ }
+ },
+ defineClass: function () {
+ function Class(content) {
+ extend(this, content);
+ }
+ return Class;
+ }, ignoreTransaction: function (scopeFunc) {
+ return PSD.trans ?
+ usePSD(PSD.transless, scopeFunc) :
+ scopeFunc();
+ }, vip: vip, async: function (generatorFn) {
+ return function () {
+ try {
+ var rv = awaitIterator(generatorFn.apply(this, arguments));
+ if (!rv || typeof rv.then !== 'function')
+ return DexiePromise.resolve(rv);
+ return rv;
+ }
+ catch (e) {
+ return rejection(e);
+ }
+ };
+ }, spawn: function (generatorFn, args, thiz) {
+ try {
+ var rv = awaitIterator(generatorFn.apply(thiz, args || []));
+ if (!rv || typeof rv.then !== 'function')
+ return DexiePromise.resolve(rv);
+ return rv;
+ }
+ catch (e) {
+ return rejection(e);
+ }
+ },
+ currentTransaction: {
+ get: function () { return PSD.trans || null; }
+ }, waitFor: function (promiseOrFunction, optionalTimeout) {
+ var promise = DexiePromise.resolve(typeof promiseOrFunction === 'function' ?
+ Dexie.ignoreTransaction(promiseOrFunction) :
+ promiseOrFunction)
+ .timeout(optionalTimeout || 60000);
+ return PSD.trans ?
+ PSD.trans.waitFor(promise) :
+ promise;
+ },
+ Promise: DexiePromise,
+ debug: {
+ get: function () { return debug; },
+ set: function (value) {
+ setDebug(value);
+ }
+ },
+ derive: derive, extend: extend, props: props, override: override,
+ Events: Events, on: globalEvents, liveQuery: liveQuery, extendObservabilitySet: extendObservabilitySet,
+ getByKeyPath: getByKeyPath, setByKeyPath: setByKeyPath, delByKeyPath: delByKeyPath, shallowClone: shallowClone, deepClone: deepClone, getObjectDiff: getObjectDiff, cmp: cmp, asap: asap$1,
+ minKey: minKey,
+ addons: [],
+ connections: connections,
+ errnames: errnames,
+ dependencies: domDeps, cache: cache,
+ semVer: DEXIE_VERSION, version: DEXIE_VERSION.split('.')
+ .map(function (n) { return parseInt(n); })
+ .reduce(function (p, c, i) { return p + (c / Math.pow(10, i * 2)); }) }));
+ Dexie.maxKey = getMaxKey(Dexie.dependencies.IDBKeyRange);
+
+ if (typeof dispatchEvent !== 'undefined' && typeof addEventListener !== 'undefined') {
+ globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, function (updatedParts) {
+ if (!propagatingLocally) {
+ var event_1;
+ event_1 = new CustomEvent(STORAGE_MUTATED_DOM_EVENT_NAME, {
+ detail: updatedParts
+ });
+ propagatingLocally = true;
+ dispatchEvent(event_1);
+ propagatingLocally = false;
+ }
+ });
+ addEventListener(STORAGE_MUTATED_DOM_EVENT_NAME, function (_a) {
+ var detail = _a.detail;
+ if (!propagatingLocally) {
+ propagateLocally(detail);
+ }
+ });
+ }
+ function propagateLocally(updateParts) {
+ var wasMe = propagatingLocally;
+ try {
+ propagatingLocally = true;
+ globalEvents.storagemutated.fire(updateParts);
+ signalSubscribersNow(updateParts, true);
+ }
+ finally {
+ propagatingLocally = wasMe;
+ }
+ }
+ var propagatingLocally = false;
+
+ var bc;
+ var createBC = function () { };
+ if (typeof BroadcastChannel !== 'undefined') {
+ createBC = function () {
+ bc = new BroadcastChannel(STORAGE_MUTATED_DOM_EVENT_NAME);
+ bc.onmessage = function (ev) { return ev.data && propagateLocally(ev.data); };
+ };
+ createBC();
+ if (typeof bc.unref === 'function') {
+ bc.unref();
+ }
+ globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, function (changedParts) {
+ if (!propagatingLocally) {
+ bc.postMessage(changedParts);
+ }
+ });
+ }
+
+ if (typeof addEventListener !== 'undefined') {
+ addEventListener('pagehide', function (event) {
+ if (!Dexie$1.disableBfCache && event.persisted) {
+ if (debug)
+ console.debug('Dexie: handling persisted pagehide');
+ bc === null || bc === void 0 ? void 0 : bc.close();
+ for (var _i = 0, connections_1 = connections; _i < connections_1.length; _i++) {
+ var db = connections_1[_i];
+ db.close({ disableAutoOpen: false });
+ }
+ }
+ });
+ addEventListener('pageshow', function (event) {
+ if (!Dexie$1.disableBfCache && event.persisted) {
+ if (debug)
+ console.debug('Dexie: handling persisted pageshow');
+ createBC();
+ propagateLocally({ all: new RangeSet(-Infinity, [[]]) });
+ }
+ });
+ }
+
+ function add(value) {
+ return new PropModification({ add: value });
+ }
+
+ function remove(value) {
+ return new PropModification({ remove: value });
+ }
+
+ function replacePrefix(a, b) {
+ return new PropModification({ replacePrefix: [a, b] });
+ }
+
+ DexiePromise.rejectionMapper = mapError;
+ setDebug(debug);
+
+ var namedExports = /*#__PURE__*/Object.freeze({
+ __proto__: null,
+ Dexie: Dexie$1,
+ liveQuery: liveQuery,
+ Entity: Entity,
+ cmp: cmp,
+ PropModification: PropModification,
+ replacePrefix: replacePrefix,
+ add: add,
+ remove: remove,
+ 'default': Dexie$1,
+ RangeSet: RangeSet,
+ mergeRanges: mergeRanges,
+ rangesOverlap: rangesOverlap
+ });
+
+ __assign(Dexie$1, namedExports, { default: Dexie$1 });
+
+ return Dexie$1;
+
+}));
+//# sourceMappingURL=dexie.js.map
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..eaa8efc
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,29 @@
+# deskflow
+
+#### 介绍
+
+deskflow
+
+### 激活windows虚拟环境
+
+- & .venv\Scripts\Activate.ps1
+
+#### 软件架构
+
+- [https://pywebview.idepy.com/]
+
+#### 安装库
+
+- docs/venv.md
+
+#### 安装教程
+
+1. xxxx
+2. xxxx
+3. xxxx
+
+#### 使用说明
+
+1. xxxx
+2. xxxx
+3. xxxx
diff --git a/docs/app_JsToPy.html b/docs/app_JsToPy.html
new file mode 100644
index 0000000..2ff1b95
--- /dev/null
+++ b/docs/app_JsToPy.html
@@ -0,0 +1,108 @@
+
+
+
+
+
+ PyWebView 双向通信
+
+
+
+
+
+
Python ↔ JS 双向通信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/app_JsToPy.py b/docs/app_JsToPy.py
new file mode 100644
index 0000000..109be3e
--- /dev/null
+++ b/docs/app_JsToPy.py
@@ -0,0 +1,77 @@
+import webview
+import threading
+import time,os
+
+CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
+
+# ---------------------- 1. 定义供 JS 调用的 Python 函数 ----------------------
+class Api:
+ """暴露给 JS 的 Python 接口类(所有方法都会被 JS 访问)"""
+ def __init__(self):
+ self.window = None # 保存窗口实例,用于 Python 主动调用 JS
+
+ def set_window(self, window):
+ """初始化时绑定窗口实例"""
+ self.window = window
+
+ def python_hello(self, name):
+ """JS 调用的同步函数:接收参数,返回结果"""
+ print(f"[Python] 收到 JS 调用,参数:{name}")
+ return f"Hello {name}! 这是 Python 返回的消息"
+
+ def python_async_task(self, duration):
+ """JS 调用的异步函数:模拟耗时任务(不阻塞前端)"""
+ def async_task():
+ print(f"[Python] 开始异步任务,耗时 {duration} 秒")
+ time.sleep(duration)
+ # 异步任务完成后,主动调用 JS 函数通知结果
+ self.window.evaluate_js(f'js_receive_python_msg("异步任务完成!耗时 {duration} 秒")')
+ print(f"[Python] 异步任务结束")
+
+ # 启动子线程执行异步任务,避免阻塞 UI
+ threading.Thread(target=async_task, daemon=True).start()
+ return "异步任务已启动,请等待结果通知"
+
+ def python_close_window(self):
+ """JS 调用关闭窗口"""
+ print("[Python] 收到关闭窗口请求")
+ self.window.destroy()
+
+# ---------------------- 2. Python 主动调用 JS 函数 ----------------------
+def python_call_js_periodically(window):
+ """Python 定时调用 JS 函数(模拟主动推送数据)"""
+ count = 0
+ while window.is_alive():
+ count += 1
+ # 执行 JS 函数,传递参数
+ window.evaluate_js(f'js_receive_python_msg("Python 主动推送:第 {count} 条消息")')
+ time.sleep(3) # 每 3 秒推送一次
+ if count >= 5:
+ break
+
+# ---------------------- 3. 启动窗口 ----------------------
+if __name__ == "__main__":
+ # 创建 API 实例
+ api = Api()
+
+ # 加载本地 HTML 文件(也可加载远程 URL:url="https://xxx.com")
+ window = webview.create_window(
+ title="PyWebView 双向通信示例",
+ # url="app_JsToPy.html", # 前端页面路径
+ url=f"file:///{CURRENT_DIR}/app_JsToPy.html",
+ resizable=True,
+ js_api=api # 关键:暴露 Python API 给 JS
+ )
+
+ # 绑定窗口实例到 API(供异步任务调用 JS)
+ api.set_window(window)
+
+ # 启动 Python 主动调用 JS 的线程(非阻塞)
+ threading.Thread(target=python_call_js_periodically, args=(window,), daemon=True).start()
+
+ # 运行窗口(阻塞主线程)
+ webview.start(
+ private_mode=False, # WebRTC必需关闭私有模式
+ debug=True, # 开发环境开启调试,生产环境关闭
+ http_server=True # 启用内置 HTTP 服务器(加载本地 HTML 必需)
+ )
\ No newline at end of file
diff --git a/docs/releaseNotes.md b/docs/releaseNotes.md
new file mode 100644
index 0000000..04d4373
--- /dev/null
+++ b/docs/releaseNotes.md
@@ -0,0 +1,17 @@
+# 版本日志
+
+## 2026.03.
+
+## 2026.03.16
+### 各端口规划
+- exe:windows端口,作为被控端
+- apk:安卓端,作为主控端
+- web:websocket接口和主控端
+### 功能
+- 全局
+ - 三端都用手机号生成唯一的uuid, 已这个uuid作为控制唯一设备码
+ - 剪切板同步;
+- 局部
+ - exe:用python获取windows屏幕和系统音频转化之后转换未webrtc媒体流
+ - web:web接口,生成 websocket通信/ice交换接口
+ - apk:用python获取Android屏幕和系统音频转化之后转换未webrtc媒体流
\ No newline at end of file
diff --git a/docs/venv.md b/docs/venv.md
new file mode 100644
index 0000000..d59ea1e
--- /dev/null
+++ b/docs/venv.md
@@ -0,0 +1,14 @@
+
+# 环境安装
+
+- pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade loguru pywebview aiortc fastapi pyinstaller uvicorn[standard] requests pystray pillow pywin32 buildozer opencv-python numpy mss
+
+
+
+
+
+
+# 涉及文档
+
+- [https://pywebview.idepy.com/guide/usage.html]
+- [https://segmentfault.com/a/1190000046888066]
\ No newline at end of file
diff --git a/docs/webrtc_demo_callee.html b/docs/webrtc_demo_callee.html
new file mode 100644
index 0000000..463fbf1
--- /dev/null
+++ b/docs/webrtc_demo_callee.html
@@ -0,0 +1,340 @@
+
+
+
+
+
+ WebRTC 接收方
+
+
+
+
+ WebRTC 接收方
+
+
+
+
本地视频
+
+
+
+
远程视频
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/webrtc_demo_caller.html b/docs/webrtc_demo_caller.html
new file mode 100644
index 0000000..48ea426
--- /dev/null
+++ b/docs/webrtc_demo_caller.html
@@ -0,0 +1,339 @@
+
+
+
+
+
+ WebRTC 请求方
+
+
+
+
+ WebRTC 请求方
+
+
+
+
本地视频
+
+
+
+
远程视频
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/icon.ico b/icon.ico
new file mode 100644
index 0000000..234fb8a
Binary files /dev/null and b/icon.ico differ
diff --git a/module/__pycache__/record_screen_to_mp4.cpython-312.pyc b/module/__pycache__/record_screen_to_mp4.cpython-312.pyc
new file mode 100644
index 0000000..deebb49
Binary files /dev/null and b/module/__pycache__/record_screen_to_mp4.cpython-312.pyc differ
diff --git a/module/aiortc_parse.py b/module/aiortc_parse.py
new file mode 100644
index 0000000..fe63ae5
--- /dev/null
+++ b/module/aiortc_parse.py
@@ -0,0 +1,287 @@
+
+import asyncio
+import json
+import threading
+import cv2
+import numpy as np
+import win32gui
+import win32ui
+import win32con
+import win32api
+
+import aiortc
+from aiortc import MediaStreamTrack, RTCPeerConnection, RTCSessionDescription
+from aiortc.contrib.media import MediaBlackhole, MediaPlayer, MediaRecorder
+from aiortc.sdp import SessionDescription
+import webview
+
+# -------------------------- 2. 兼容VideoFrame导入 --------------------------
+try:from aiortc.contrib.media import VideoFrame,AudioFrame
+except ImportError:from aiortc import VideoFrame,AudioFrame
+
+
+# -------------------------- 1. Windows 11高DPI适配 --------------------------
+ctypes.windll.shcore.SetProcessDpiAwareness(2) # 解决高分屏采集偏移问题
+
+# -------------------------- 2. 兼容VideoFrame导入 --------------------------
+try:from aiortc.contrib.media import VideoFrame,AudioFrame
+except ImportError:from aiortc import VideoFrame,AudioFrame
+
+# -------------------------- 2. 屏幕视频采集轨道 --------------------------
+class ScreenVideoTrack(MediaStreamTrack):
+ kind = "video"
+ def __init__(self, fps=30, scale_factor=1.0):
+ super().__init__()
+ self.fps = fps
+ self.scale_factor = scale_factor
+ self.stop_flag = False
+ self.pts = 0
+ self.frame_interval = 1.0 / fps
+
+ # 获取真实屏幕分辨率(适配高DPI)
+ self.user32 = ctypes.windll.user32
+ self.screen_width = self.user32.GetSystemMetrics(0)
+ self.screen_height = self.user32.GetSystemMetrics(1)
+
+ def capture_screen(self):
+ """Windows 11原生API采集屏幕(低延迟)"""
+ left, top, width, height = 0, 0, self.screen_width, self.screen_height
+
+ # 1. 创建设备上下文(DC)
+ hdesktop = win32gui.GetDesktopWindow()
+ hwnd_dc = win32gui.GetWindowDC(hdesktop)
+ mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc)
+ save_dc = mfc_dc.CreateCompatibleDC()
+
+ # 2. 复制屏幕内容到位图
+ save_bitmap = win32ui.CreateBitmap()
+ save_bitmap.CreateCompatibleBitmap(mfc_dc, width, height)
+ save_dc.SelectObject(save_bitmap)
+ save_dc.BitBlt((0, 0), (width, height), mfc_dc, (left, top), win32con.SRCCOPY)
+
+ # 3. 转换为numpy数组(BGR格式)
+ bmp_data = save_bitmap.GetBitmapBits(True)
+ frame = np.frombuffer(bmp_data, dtype=np.uint8).reshape((height, width, 4))
+ frame = frame[:, :, :3] # 去掉Alpha通道
+ frame = frame[:, :, ::-1] # BGRA → BGR(适配aiortc)
+
+ # 4. 缩放(可选,降低文件大小)
+ if self.scale_factor != 1.0:
+ new_width = int(width * self.scale_factor)
+ new_height = int(height * self.scale_factor)
+ frame = cv2.resize(frame, (new_width, new_height), interpolation=cv2.INTER_AREA)
+
+ # 5. 释放资源(避免内存泄漏)
+ win32gui.DeleteObject(save_bitmap.GetHandle())
+ save_dc.DeleteDC()
+ mfc_dc.DeleteDC()
+ win32gui.ReleaseDC(hdesktop, hwnd_dc)
+
+ return frame
+
+ async def recv(self):
+ """aiortc核心方法:持续返回视频帧"""
+ if self.stop_flag:
+ raise StopAsyncIteration
+ # 控制帧率
+ await asyncio.sleep(self.frame_interval)
+ # 获取windows11 原始的视频帧
+ frame_data = self.capture_screen()
+
+ # 转换为aiortc VideoFrame
+ video_frame = self._convert_to_video_frame(frame_data)
+
+ video_frame.pts = int(self.pts)
+ video_frame.time_base = np.array([1, self.fps])
+ self.pts += 1
+
+ return video_frame
+
+ def _convert_to_video_frame(self, img):
+ """将numpy数组转换为aiortc的VideoFrame"""
+ return VideoFrame.from_ndarray(img, format="bgr24")
+
+ def stop(self):
+ """停止屏幕采集"""
+ self.stop_flag = True
+
+# -------------------------- 3. 系统音频采集轨道 --------------------------
+class SystemAudioTrack(MediaStreamTrack):
+ kind = "audio"
+
+ def __init__(self, sample_rate=48000, channels=2):
+ super().__init__()
+ self.sample_rate = sample_rate
+ self.channels = channels
+ self.stop_flag = False
+ self.pts = 0
+ self.audio_queue = asyncio.Queue(maxsize=10) # 音频数据队列
+
+ # 启动麦克风/系统音频采集(这里默认采集麦克风,系统音频见备注)
+ self._start_audio_capture()
+
+ def _start_audio_capture(self):
+ """启动音频采集(麦克风)"""
+ def audio_callback(indata, frames, time, status):
+ if status or self.stop_flag:
+ return
+ # 将音频数据转为float32格式(aiortc要求)
+ audio_data = indata.astype(np.float32)
+ self.audio_queue.put_nowait(audio_data)
+
+ # 打开音频输入流
+ self.audio_stream = sd.InputStream(
+ samplerate=self.sample_rate,
+ channels=self.channels,
+ callback=audio_callback,
+ blocksize=1024
+ )
+ self.audio_stream.start()
+
+ async def recv(self):
+ """aiortc核心方法:持续返回音频帧"""
+ if self.stop_flag and self.audio_queue.empty():
+ raise StopAsyncIteration
+
+ # 从队列获取音频数据
+ audio_data = await self.audio_queue.get()
+ sample_count = len(audio_data)
+
+ # 转换为aiortc AudioFrame
+ audio_frame = AudioFrame(
+ samples=audio_data.T, # 转置为 (channels, samples)
+ sample_rate=self.sample_rate,
+ channels=self.channels
+ )
+ audio_frame.pts = int(self.pts)
+ audio_frame.time_base = np.array([1, self.sample_rate])
+ self.pts += sample_count
+
+ return audio_frame
+
+ def stop(self):
+ """停止音频采集"""
+ self.stop_flag = True
+ self.audio_stream.stop()
+ self.audio_stream.close()
+
+
+class WebRTCService:
+ def __init__(self):
+ super().__init__()
+ self.peer=None
+ self.channel = None
+ self.screen_track = None
+ self.loop = asyncio.new_event_loop()
+ self.thread = None
+
+ def start_async_loop(self):
+ """在子线程运行asyncio循环"""
+ asyncio.set_event_loop(self.loop)
+ self.loop.run_forever()
+
+ # 1. 初始化 RTCPeerConnection
+ async def init_connection(self):
+ # 初始化 RTCPeerConnection
+ if self.peer :self.peer =None
+ # 创建 RTCPeerConnection 实例
+ self.peer = RTCPeerConnection()
+
+ # 添加事件监听器
+ @self.peer.on("connectionstatechange")
+ async def on_connectionstatechange():
+ print(f"连接状态: {self.peer.connectionState}")
+
+ async def recv(self):
+ """捕获屏幕帧并转换为 aiortc 可处理的 VideoFrame"""
+ if not self.running:
+ raise RuntimeError("Track stopped")
+
+ # 1. 捕获屏幕(返回 numpy 数组)
+ img = np.array(self.sct.grab(self.monitor))
+ # 2. 转换格式:mss 捕获的是 BGRA,转为 BGR(OpenCV 格式)
+ img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)
+ # 3. 转换为 VideoFrame(aiortc 要求的格式)
+ frame = VideoFrame.from_ndarray(img, format="bgr24")
+
+ # 调整时间戳(保证流的时序)
+ frame.pts = self.pts
+ frame.time_base = self.time_base
+ self.pts += 1
+
+ return frame
+
+ # 2. 获取用户媒体
+ async def setup_media(self):
+ # 使用摄像头和麦克风
+ # player = MediaPlayer('/dev/video0') # Linux 摄像头
+ # 或者使用文件
+ media_flag = True
+ if media_flag:
+ player = MediaPlayer('screen_60fps.mp4')
+ return player.audio, player.video
+ else:
+ player_video = ScreenVideoTrack(fps=30)
+ player_audio = SystemAudioTrack()
+ return player_audio, player_video
+
+ # 3. 创建 Offer
+ async def create_offer(self):
+ # 添加音视频轨道
+ audio_track, video_track = await self.setup_media()
+
+ if audio_track:
+ self.peer.addTrack(audio_track)
+ if video_track:
+ self.peer.addTrack(video_track)
+
+ # 创建 offer
+ offer = await self.peer.createOffer()
+ await self.peer.setLocalDescription(offer)
+
+ return offer
+
+ # 3. 创建 接收 offer 并创建 Answer
+ async def handle_remote_offer(self, offer_sdp):
+ # 设置远程描述
+ offer = RTCSessionDescription(sdp=offer_sdp, type="offer")
+ await self.peer.setRemoteDescription(offer)
+
+ # 创建 answer
+ answer = await self.peer.createAnswer()
+ await self.peer.setLocalDescription(answer)
+ return answer
+
+ async def handle_remote_answer(self,answer_sdp):
+ await self.peer.setRemoteDescription(obj)
+
+ # 监听ICE候选,发送到信令服务器
+ @self.peer.on("icecandidate")
+ async def on_icecandidate(candidate):
+ if candidate:
+ # await signaling.send(candidate)
+ pass
+
+ # 4. 数据通道使用
+ async def setup_data_channel(self,pc):
+ # 初始化 数据通道
+ if self.channel : self.channel = None
+ # 创建数据通道
+ self.channel = self.peer.createDataChannel("chat")
+
+ @self.channel.on("open")
+ def on_open():
+ print("数据通道已打开")
+ # 发送消息
+ self.channel.send("Hello from aiortc!")
+
+ @self.channel.on("message")
+ def on_message(message):
+ print(f"收到消息: {message}")
+
+ return self.channel
+
+if __name__ == "__main__":
+ rtc=rtc()
+ rtc.setup_media()
+ pass
diff --git a/module/main.js b/module/main.js
new file mode 100644
index 0000000..600e352
--- /dev/null
+++ b/module/main.js
@@ -0,0 +1,68 @@
+// 导入类(ES6 模块化)
+import { webSocket_ModuleSimple } from "./newWebRtcSync.js";
+
+class rtcCallee {
+ constructor(userIdentity, user_id) {
+ this.current_time = new Date();
+ this.identity = userIdentity;
+ // websocket对象
+ this.wsClient = null;
+ // WebRTC 核心对象
+ this.rtcPeer = null;
+ this.dataChannel = null;
+ this.state;
+ // webRTC 信令
+ this.clientCandidates = [];
+ this.remoteOffer;
+ this.localAnswer;
+ this.localStream;
+ this.remoteStream;
+ this._init_ws(user_id);
+ }
+ _init_ws(user_id) {
+ // websocket初始化
+ if (this.wsClient) {
+ this.wsClient = null;
+ }
+ console.info(`初始化websocket`);
+ // const wsService = `ws://localhost:8000/wsSignaling/${user_id}/`
+ const wsService = `wss://api.vlos.net/wsSignaling/${user_id}/`;
+ this.wsClient = new webSocket_ModuleSimple(wsService);
+ this.wsClient.connect();
+ this.handlerWsSignalingLisioner();
+ }
+ /**
+ * 监听websocket信息 回调函数方式监控websocket数据
+ */
+ async handlerWsSignalingLisioner() {
+ this.wsClient.on({
+ onMessage: async (msg) => {
+ const res_dict = JSON.parse(msg);
+ console.info(
+ `[exportWebRtcCallee.js]->[handlerWsSignalingLisioner函数][${this.identity}][${this.current_time.toLocaleTimeString()}]>>>收到信息`,
+ "type:",
+ res_dict.type,
+ "长度",
+ msg.length,
+ );
+ switch (res_dict.type) {
+ case "candidate":
+ console.info(
+ `[exportWebRtcCallee.js][${this.identity}][${this.current_time.toLocaleTimeString()}]>>>监听candidate`,
+ res_dict.data,
+ );
+ break;
+ case "offer":
+ this.remoteOffer = res_dict.data;
+ console.info(
+ `[exportWebRtcCallee.js][${this.identity}][${this.current_time.toLocaleTimeString()}]>>>收到远程offer`,
+ );
+ console.warn(`收到的offer,`, this.remoteOffer);
+ break;
+ }
+ },
+ });
+ }
+}
+
+new rtcCallee("callee", 123);
diff --git a/module/newWebRtcSync.js b/module/newWebRtcSync.js
new file mode 100644
index 0000000..fe6bf11
--- /dev/null
+++ b/module/newWebRtcSync.js
@@ -0,0 +1,609 @@
+/**
+ * 【发起方】
+1. 创建 PeerConnection
+2. 采集本地流并添加到 PeerConnection
+3. 调用 createOffer() → 生成Offer
+4. 调用 setLocalDescription(offer) → 开始收集ICE候选
+5. 通过信令发送Offer给接收方
+6. 监听 onicecandidate → 发送ICE候选给接收方
+7. 接收接收方的Answer → 调用 setRemoteDescription(answer)
+8. 接收接收方的ICE候选 → 调用 addIceCandidate(candidate)
+
+【接收方】
+1. 创建 PeerConnection
+2. 接收发起方的Offer → 调用 setRemoteDescription(offer)
+3. 调用 createAnswer() → 生成Answer
+4. 调用 setLocalDescription(answer) → 开始收集ICE候选
+5. 通过信令发送Answer给发起方
+6. 监听 onicecandidate → 发送ICE候选给发起方
+7. 接收发起方的ICE候选 → 调用 addIceCandidate(candidate)
+ */
+
+/**
+ * WebRTC 核心封装类(ES6 模块化)
+ * 支持音视频互通、自定义数据传输、外部流管理
+ * 开箱即用,无需额外依赖
+ */
+export class WebRTC_ModuleSimple {
+ /**
+ * 构造函数
+ * @param {Object} options 初始化配置
+ * @param {HTMLElement} options.localVideo 本地视频播放元素
+ * @param {HTMLElement} options.remoteVideo 远端视频播放元素
+ * @param {string} options.dataChannelLabel 数据通道标签(默认 'chat')
+ * @param {Function} receivepeerCallback 回调函数
+ */
+ constructor(options = {}) {
+ this.current_time = new Date();
+ // 基础配置
+ this.config = {
+ localVideo: options.localVideo,
+ remoteVideo: options.remoteVideo,
+ dataChannelLabel: options.dataChannelLabel || "chat",
+ iceServers: [
+ { urls: "stun:stun.l.google.com:19302" },
+ { urls: "stun:stun.cloudflare.com:3478" },
+ ],
+ };
+ // WebRTC 核心对象
+ this.peerConnection = null;
+ this.dataChannel = null;
+ // 媒体流管理
+ this.localStream = null; // 本地流(可外部传入/获取)
+ this.remoteStream = null; // 远端流(可外部获取)
+
+ // 事件回调(外部可自定义)
+ this.callbacks = {
+ onIceCandidate: (candidate) => {}, // ICE 候选生成
+ onConnectSuccess: () => {}, // 连接成功
+ onConnectFailed: () => {}, // 连接失败
+ onRemoteStream: (stream) => {}, // 收到远端流
+ onMessage: (data) => {}, // 收到自定义消息
+ onClose: () => {}, // 连接关闭
+ onState: (state) => {},
+ };
+ // 初始化 PeerConnection
+ this._initPeerConnection();
+ }
+ /**
+ * 初始化 RTCPeerConnection 核心对象
+ * @private
+ */
+ _initPeerConnection() {
+ try {
+ // 销毁旧实例
+ if (this.peerConnection) {
+ this.peerConnection.close();
+ }
+ // 浏览器兼容处理
+ const RTCPeerConnection =
+ window.RTCPeerConnection ||
+ window.mozRTCPeerConnection ||
+ window.webkitRTCPeerConnection;
+ this.peerConnection = new RTCPeerConnection({
+ iceServers: this.config.iceServers,
+ });
+ // 监听信令协商状态
+ this.signalingStateListener();
+ // 监听远程信息
+ this.peerConnectionLisioner();
+ } catch (error) {
+ console.error("WebRTC 初始化失败:", error);
+ this.callbacks.onConnectFailed();
+ }
+ }
+ // 监听信令状态变化(Offer/Answer/Ice 协商)
+ signalingStateListener() {
+ // 初始状态打印
+ console.warn(
+ `[webRTC]${this.current_time.toLocaleTimeString()}初始信令状态:`,
+ this.peerConnection.signalingState,
+ );
+ // 监听信令状态变化(Offer/Answer 协商)
+ this.peerConnection.onsignalingstatechange = () => {
+ /**
+ * 信令状态枚举值(全阶段)
+ * new 初始状态
+ * have-local-offer 本地已设置 Offer 发起者:已调用 createOffer() + setLocalDescription(offer)
+ * have-remote-offer 远端已设置 Offer 应答者:已调用 setRemoteDescription(offer)
+ * have-local-answer 本地已设置 Answer 应答者:已调用 createAnswer() + setLocalDescription(answer)
+ * have-remote-answer 远端已设置 Answer 发起者:已调用 setRemoteDescription(answer)
+ * stable 协商完成,稳定状态
+ * closed PeerConnection 已关闭
+ */
+ const state = this.peerConnection.signalingState;
+ console.warn(
+ `[webRTC]${this.current_time.toLocaleTimeString()}初始信令变更:`,
+ state,
+ );
+ state && this.callbacks.onState(state);
+ // 根据状态执行不同逻辑
+ switch (state) {
+ case "new":
+ console.warn(
+ `[webRTC]${this.current_time.toLocaleTimeString()}【状态】未开始协商,可创建 Offer`,
+ );
+ break;
+ case "have-local-offer":
+ console.warn(
+ `[webRTC]${this.current_time.toLocaleTimeString()}【状态】发起者已发 Offer,等待应答者 Answer`,
+ );
+ // 可触发 ICE 候选者发送逻辑
+ break;
+ case "have-remote-offer":
+ console.warn(
+ `[webRTC]${this.current_time.toLocaleTimeString()}【状态】应答者已收 Offer,可创建 Answer`,
+ );
+ break;
+ case "stable":
+ console.warn(
+ `[webRTC]${this.current_time.toLocaleTimeString()}【状态】Offer/Answer 协商完成,进入 ICE 配对阶段`,
+ );
+ break;
+ case "closed":
+ console.warn(
+ `[webRTC]${this.current_time.toLocaleTimeString()}【状态】PeerConnection 已关闭,协商终止`,
+ );
+ break;
+ default:
+ console.warn(
+ `[webRTC]${this.current_time.toLocaleTimeString()}【状态】协商中:`,
+ state,
+ );
+ }
+ };
+ // ICE 候选者收集 / 配对 / 连接
+ this.peerConnection.onicegatheringstatechange = () => {
+ /**
+ * ICE 连接状态(iceConnectionState)
+ * new 初始状态,未开始连接
+ * checking 正在检查候选者配对
+ * connected 已建立连接(P2P 成功)
+ * completed 连接完成(所有候选者检查完毕)
+ * failed ICE 协商失败
+ * disconnected 连接断开(临时)
+ * closed PeerConnection 已关闭
+ */
+ const state = this.peerConnection.iceGatheringState;
+ console.warn(
+ `[webRTC]${this.current_time.toLocaleTimeString()}ICE 收集状态变更:`,
+ state,
+ );
+ state && this.callbacks.onState(state);
+ switch (state) {
+ case "gathering":
+ console.warn(
+ `[webRTC]${this.current_time.toLocaleTimeString()}【ICE】开始收集候选者(host/srflx/relay)`,
+ state,
+ );
+ break;
+ case "complete":
+ console.warn(
+ `[webRTC]${this.current_time.toLocaleTimeString()}【ICE】候选者收集完成`,
+ state,
+ );
+ break;
+ }
+ };
+ }
+ peerConnectionLisioner() {
+ if (!this.peerConnection) {
+ return;
+ }
+ // 监听远端媒体流
+ this.peerConnection.ontrack = (e) => {
+ this.remoteStream = e.streams[0];
+ // 绑定远端视频
+ if (this.config.remoteVideo) {
+ this.config.remoteVideo.srcObject = this.remoteStream;
+ this.config.remoteVideo.play().catch(() => {});
+ }
+ this.callbacks.onRemoteStream(this.remoteStream);
+ };
+ // 监听peer连接状态
+ this.peerConnection.onconnectionstatechange = () => {
+ const state = this.peerConnection.connectionState;
+ if (state === "connected") {
+ this.callbacks.onConnectSuccess();
+ } else if (state === "failed") {
+ this.callbacks.onConnectFailed();
+ } else if (state === "closed") {
+ this.callbacks.onClose();
+ }
+ };
+ // 监听远端创建的数据通道
+ this.peerConnection.ondatachannel = (e) => {
+ this.dataChannel = e.channel;
+ this.dataChannelLisioner();
+ };
+ // 监听 ICE 候选(需通过信令服务器发给对方)
+ this.peerConnection.onicecandidate = (e) => {
+ e.candidate && this.callbacks.onIceCandidate(e.candidate);
+ };
+ // 监听 ICE 连接状态,排查失败原因
+ this.peerConnection.oniceconnectionstatechange = () => {
+ const state = this.peerConnection.iceConnectionState;
+ console.info("ICE 连接状态:", state);
+ // 捕获 ICE failed 状态,给出明确提示
+ if (state === "failed") {
+ console.error("ICE 协商失败!请检查 TURN 服务器配置或网络环境");
+ // 可选:触发重新协商
+ this.createOffer();
+ }
+ };
+ // 新监听 ICE 候选者生成错误
+ this.peerConnection.onicecandidateerror = (event) => {
+ console.error(
+ "ICE 候选者生成失败:",
+ event.errorText,
+ "错误码:",
+ event.errorCode,
+ );
+ };
+ }
+ /**
+ * 初始化数据通道(自定义消息传输)
+ * @private
+ */
+ dataChannelLisioner() {
+ if (!this.dataChannel) return;
+ // 数据通道打开
+ this.dataChannel.onopen = () => console.info("数据通道已就绪");
+
+ // 接收自定义消息(自动解析JSON)
+ this.dataChannel.onmessage = (e) => {
+ let data = e.data;
+ try {
+ data = JSON.parse(data);
+ } catch (err) {}
+ this.callbacks.onMessage(data);
+ this.dataChannelSendMsg(data);
+ };
+ // 数据通道错误
+ this.dataChannel.onerror = (error) => console.error("数据通道异常:", error);
+ // 数据通道关闭
+ this.dataChannel.onclose = () => console.info("数据通道关闭");
+ }
+ /**
+ * 采集本地音视频流(一键调用)
+ * @param {Object} constraints 媒体约束(默认开启音视频)
+ * @returns {Promise} 本地媒体流
+ */
+ async captureLocalStream(constraints = { video: true, audio: true }) {
+ try {
+ // getUserMedia 设备硬件:摄像头(视频)、麦克风(音频)
+ // getDisplayMedia 屏幕 / 窗口 / 标签页:显示器内容(视频),可选采集系统音频
+ this.localStream =
+ await navigator.mediaDevices.getDisplayMedia(constraints);
+ // 绑定本地视频
+ if (this.config.localVideo) {
+ this.config.localVideo.srcObject = this.localStream;
+ this.config.localVideo.muted = true; // 静音避免回声
+ this.config.localVideo.play().catch(() => {});
+ }
+ // 将流添加到连接
+ this.localStream.getTracks().forEach((track) => {
+ this.peerConnection.addTrack(track, this.localStream);
+ });
+ return this.localStream;
+ } catch (error) {
+ console.error("采集本地流失败:", error);
+ throw new Error(`媒体权限不足或设备不可用: ${error.message}`);
+ }
+ }
+
+ /**
+ * 外部传入本地媒体流(支持屏幕共享等自定义流)
+ * @param {MediaStream} stream 本地媒体流
+ */
+ setLocalStream(stream) {
+ if (!stream) throw new Error("媒体流不能为空");
+
+ this.localStream = stream;
+ // 绑定视频(可选)
+ if (this.config.localVideo) {
+ this.config.localVideo.srcObject = stream;
+ this.config.localVideo.muted = true;
+ this.config.localVideo.play().catch(() => {});
+ }
+ // 添加到连接
+ // stream.getTracks().forEach(track => {
+ // this.peerConnection.addTrack(track, stream);
+ // });
+ }
+
+ /**
+ * 获取本地媒体流
+ * @returns {MediaStream|null} 本地流
+ */
+ getLocalStream() {
+ return this.localStream;
+ }
+
+ /**
+ * 获取远端媒体流
+ * @returns {MediaStream|null} 远端流
+ */
+ getRemoteStream() {
+ return this.remoteStream;
+ }
+
+ /**
+ * 发起方:创建 Offer 并开启数据通道
+ * @returns {Promise} Offer 对象
+ */
+ async createOffer() {
+ // 重置数据通道
+ if (this.dataChannel) {
+ this.dataChannel.close();
+ this.dataChannel = null;
+ }
+ // 创建数据通道(发起方主动创建)
+ this.dataChannel = this.peerConnection.createDataChannel(
+ this.config.dataChannelLabel,
+ );
+ // 监听数据通道
+ this.dataChannelLisioner();
+
+ const offer = await this.peerConnection.createOffer();
+ // 设置本地offer
+ await this.peerConnection.setLocalDescription(offer);
+ return offer;
+ }
+
+ /**
+ * 接收方:处理远端 Offer 并创建 Answer
+ * @param {RTCSessionDescription} offer 远端 Offer
+ * @returns {Promise} Answer 对象
+ */
+ async handleRemoteOffer(offer) {
+ await this.peerConnection.setRemoteDescription(
+ new RTCSessionDescription(offer),
+ );
+ const answer = await this.peerConnection.createAnswer();
+ await this.peerConnection.setLocalDescription(answer);
+ return answer;
+ }
+
+ /**
+ * 发起方:处理远端 Answer
+ * @param {RTCSessionDescription} answer 远端 Answer
+ */
+ async handleAnswer(answer) {
+ try {
+ // 检查当前信令状态,仅在非 stable 时设置
+ if (this.peerConnection.signalingState !== "stable") {
+ await this.peerConnection.setRemoteDescription(
+ new RTCSessionDescription(answer),
+ );
+ console.info("远端 Answer 设置成功");
+ } else {
+ console.warn("PeerConnection 已处于稳定状态,无需重复设置 Answer");
+ }
+ } catch (error) {
+ console.error("设置远端 Answer 失败:", error);
+ }
+ }
+
+ /**
+ * 添加远端 ICE 候选
+ * @param {RTCIceCandidate} candidate ICE 候选对象
+ */
+ async addIceCandidate(candidate) {
+ if (!candidate) return;
+ try {
+ if (this.peerConnection && candidate) {
+ await this.peerConnection.addIceCandidate(
+ new RTCIceCandidate(candidate),
+ );
+ console.info("ICE 候选者添加成功", candidate);
+ } else return;
+ } catch (error) {
+ console.info(
+ "操作失败,需要先设置【远端的SDP】:步骤1:先设置远端 Offer(发起者的 SDP)步骤2:再添加 ICE 候选者(此时已有 remoteDescription)步骤3:创建并设置本地 Answer(依赖 remoteDescription)",
+ );
+ console.error(error);
+ if (error.message.includes("Unknown ufrag")) {
+ // ufrag 不匹配,说明是旧会话候选者,直接丢弃
+ console.warn("ICE 候选者 ufrag 不匹配,丢弃:", candidate);
+ } else {
+ // 其他错误(如未设置 remoteDescription),缓存候选者
+ console.info("缓存 ICE 候选者,待会话就绪后处理");
+ }
+ }
+ }
+ /**
+ * 发送自定义数据(支持字符串/对象)
+ * @param {string|Object} data 要发送的数据
+ */
+ dataChannelSendMsg(data) {
+ if (!this.dataChannel || this.dataChannel.readyState !== "open") {
+ throw new Error("数据通道未连接,无法发送消息");
+ }
+ const sendData = typeof data === "object" ? JSON.stringify(data) : data;
+ this.dataChannel.send(sendData);
+ }
+ /**
+ * 关闭连接并清理所有资源
+ */
+ close() {
+ // 关闭数据通道
+ if (this.dataChannel) {
+ this.dataChannel.close();
+ this.dataChannel = null;
+ }
+
+ // 关闭 PeerConnection
+ if (this.peerConnection) {
+ this.peerConnection.close();
+ this.peerConnection = null;
+ }
+
+ // 停止媒体流
+ if (this.localStream) {
+ this.localStream.getTracks().forEach((track) => track.stop());
+ this.localStream = null;
+ }
+
+ // 清空视频
+ if (this.config.localVideo) this.config.localVideo.srcObject = null;
+ if (this.config.remoteVideo) this.config.remoteVideo.srcObject = null;
+
+ this.callbacks.onClose();
+ console.log("WebRTC 连接已完全关闭");
+ }
+ /**
+ * 同步暂停指定毫秒数(阻塞主线程) 延迟一段时间运行 await this.delay(100);
+ * @param {number} ms 暂停毫秒数
+ */
+ delayAsync(ms) {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+ }
+ /**
+ * 同步暂停指定毫秒数(阻塞主线程)
+ * @param {number} ms 暂停毫秒数
+ */
+ delaySync(ms) {
+ const start = Date.now();
+ // 循环等待,直到时间差达到指定毫秒数
+ while (Date.now() - start < ms) {
+ // 空循环,消耗时间
+ }
+ }
+ /**
+ * 注册事件回调
+ * @param {Object} handlers 回调函数集合
+ * 示例:rtc.on({ onMessage: (data) => console.log(data) })
+ */
+ on(handlers) {
+ Object.keys(handlers).forEach((key) => {
+ if (this.callbacks[key] && typeof handlers[key] === "function") {
+ this.callbacks[key] = handlers[key];
+ }
+ });
+ }
+}
+
+// 封装websocket
+export class webSocket_ModuleSimple {
+ constructor(url, receiveMessageCallback = null) {
+ this.url = url;
+ this.socket = null;
+ this.heartbeatInterval = 30000; // 心跳间隔 30s
+ this.reconnectInterval = 10000; // 重连间隔 10s
+ this.maxReconnectAttempts = 5;
+ this.reconnectAttempts = 0;
+ this.heartbeatTimer = null;
+ this.stopWs = false;
+ this.receiveMessageCallback = receiveMessageCallback; // 接收消息回调函数
+ this.callbacks = {
+ onMessage: (data) => {}, // 收到自定义消息
+ };
+ }
+ connect() {
+ if (this.socket && this.socket.readyState === WebSocket.OPEN) return;
+ this.socket = new WebSocket(this.url);
+ this.socket.onopen = () => {
+ console.log("连接成功", this.url);
+ this.reconnectAttempts = 0;
+ this.startHeartbeat();
+ };
+ this.socket.onmessage = (event) => {
+ // console.log('收到消息:', event.data);
+ this.receiveMessage(event);
+ this.startHeartbeat(); // 收到消息重置心跳
+ };
+ this.socket.onclose = () => {
+ console.log("连接关闭", this.url);
+ if (!this.stopWs) this.handleReconnect();
+ };
+ this.socket.onerror = () => {
+ console.log("连接错误", this.url);
+ this.closeHeartbeat();
+ };
+ }
+ /**
+ * 同步暂停指定毫秒数(阻塞主线程) 延迟一段时间运行 await this.delay(100);
+ * @param {number} ms 暂停毫秒数
+ */
+ delayAsync(ms) {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+ }
+ /**
+ * 同步暂停指定毫秒数(阻塞主线程)
+ * @param {number} ms 暂停毫秒数
+ */
+ delaySync(ms) {
+ const start = Date.now();
+ // 循环等待,直到时间差达到指定毫秒数
+ while (Date.now() - start < ms) {
+ // 空循环,消耗时间
+ }
+ }
+ async send(message) {
+ if (this.socket?.readyState === WebSocket.OPEN) {
+ await this.delayAsync(500);
+ this.socket.send(JSON.stringify(message));
+ } else {
+ console.error("WebSocket 未连接");
+ }
+ }
+ startHeartbeat() {
+ this.closeHeartbeat();
+ this.heartbeatTimer = setInterval(() => {
+ if (this.socket?.readyState === WebSocket.OPEN) {
+ this.socket.send(JSON.stringify({ type: "heartBeat" }));
+ // console.log('发送心跳包');
+ }
+ }, this.heartbeatInterval);
+ }
+ closeHeartbeat() {
+ clearInterval(this.heartbeatTimer);
+ }
+ handleReconnect() {
+ if (this.reconnectAttempts < this.maxReconnectAttempts) {
+ setTimeout(() => {
+ console.log(`尝试重连 (${++this.reconnectAttempts})`);
+ this.connect();
+ }, this.reconnectInterval);
+ } else {
+ console.warn("达到最大重连次数,停止重连");
+ }
+ }
+ close() {
+ this.stopWs = true;
+ this.socket?.close();
+ this.closeHeartbeat();
+ }
+ /**
+ * 接收到消息
+ */
+ receiveMessage(event) {
+ // 根据业务自行处理
+ // console.info('[webSocket_ModuleSimple]receiveMessage:', event.data)
+ this.receiveMessageCallback && this.receiveMessageCallback(event.data);
+ this.callbacks.onMessage(event.data);
+ }
+ /**
+ * 注册事件回调
+ * @param {Object} handlers 回调函数集合
+ * 示例:rtc.on({ onMessage: (data) => console.log(data) })
+ */
+ on(handlers) {
+ Object.keys(handlers).forEach((key) => {
+ if (this.callbacks[key] && typeof handlers[key] === "function") {
+ this.callbacks[key] = handlers[key];
+ }
+ });
+ }
+}
+// ---------------------------------------------->
+// 验证与使用
+
+// const receiveMessage = (res) => {
+// console.log('接收消息回调:', res)
+// }
+// const wsClient = new WebSocketClient('`ws://localhost:8000/ws/0/`', receiveMessage);
+// wsClient.connect();
+// 发送业务消息
+// wsClient.send({ type: 'message', data: 'Hello Server' });
diff --git a/module/record_screen_to_mp4.py b/module/record_screen_to_mp4.py
new file mode 100644
index 0000000..02e8e52
--- /dev/null
+++ b/module/record_screen_to_mp4.py
@@ -0,0 +1,385 @@
+"""功能
+python 获取window11电脑的屏幕的视频/音频流并保存为mp4格式文件
+"""
+
+"""安装核心依赖
+& .venv/Scripts/python.exe -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade aiortc pywin32 numpy opencv-python sounddevice pycaw comtypes
+"""
+import os
+import asyncio
+import ctypes
+import numpy as np
+import win32gui
+import win32ui
+import win32con
+import win32api
+import sounddevice as sd
+from aiortc import MediaStreamTrack, RTCPeerConnection
+from aiortc.contrib.media import MediaRecorder
+
+# -------------------------- 1. Windows 11高DPI适配 --------------------------
+ctypes.windll.shcore.SetProcessDpiAwareness(2) # 解决高分屏采集偏移问题
+
+# -------------------------- 2. 兼容VideoFrame导入 --------------------------
+try:from aiortc.contrib.media import VideoFrame,AudioFrame
+except ImportError:from aiortc import VideoFrame,AudioFrame
+
+# -------------------------- 2. 屏幕视频采集轨道 --------------------------
+class ScreenVideoTrack(MediaStreamTrack):
+ kind = "video"
+ def __init__(self, fps=30, scale_factor=1.0):
+ super().__init__()
+ self.fps = fps
+ self.scale_factor = scale_factor
+ self.stop_flag = False
+ self.pts = 0
+ self.frame_interval = 1.0 / fps
+
+ # 获取真实屏幕分辨率(适配高DPI)
+ self.user32 = ctypes.windll.user32
+ self.screen_width = self.user32.GetSystemMetrics(0)
+ self.screen_height = self.user32.GetSystemMetrics(1)
+
+ def capture_screen(self):
+ """Windows 11原生API采集屏幕(低延迟)"""
+ left, top, width, height = 0, 0, self.screen_width, self.screen_height
+
+ # 1. 创建设备上下文(DC)
+ hdesktop = win32gui.GetDesktopWindow()
+ hwnd_dc = win32gui.GetWindowDC(hdesktop)
+ mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc)
+ save_dc = mfc_dc.CreateCompatibleDC()
+
+ # 2. 复制屏幕内容到位图
+ save_bitmap = win32ui.CreateBitmap()
+ save_bitmap.CreateCompatibleBitmap(mfc_dc, width, height)
+ save_dc.SelectObject(save_bitmap)
+ save_dc.BitBlt((0, 0), (width, height), mfc_dc, (left, top), win32con.SRCCOPY)
+
+ # 3. 转换为numpy数组(BGR格式)
+ bmp_data = save_bitmap.GetBitmapBits(True)
+ frame = np.frombuffer(bmp_data, dtype=np.uint8).reshape((height, width, 4))
+ frame = frame[:, :, :3] # 去掉Alpha通道
+ frame = frame[:, :, ::-1] # BGRA → BGR(适配aiortc)
+
+ # 4. 缩放(可选,降低文件大小)
+ if self.scale_factor != 1.0:
+ new_width = int(width * self.scale_factor)
+ new_height = int(height * self.scale_factor)
+ frame = cv2.resize(frame, (new_width, new_height), interpolation=cv2.INTER_AREA)
+
+ # 5. 释放资源(避免内存泄漏)
+ win32gui.DeleteObject(save_bitmap.GetHandle())
+ save_dc.DeleteDC()
+ mfc_dc.DeleteDC()
+ win32gui.ReleaseDC(hdesktop, hwnd_dc)
+
+ return frame
+
+ async def recv(self):
+ """aiortc核心方法:持续返回视频帧"""
+ if self.stop_flag:
+ raise StopAsyncIteration
+ # 控制帧率
+ await asyncio.sleep(self.frame_interval)
+ # 获取windows11 原始的视频帧
+ frame_data = self.capture_screen()
+
+ # 转换为aiortc VideoFrame
+ video_frame = self._convert_to_video_frame(frame_data)
+
+ video_frame.pts = int(self.pts)
+ video_frame.time_base = np.array([1, self.fps])
+ self.pts += 1
+
+ return video_frame
+
+ def _convert_to_video_frame(self, img):
+ """将numpy数组转换为aiortc的VideoFrame"""
+ return VideoFrame.from_ndarray(img, format="bgr24")
+
+ def stop(self):
+ """停止屏幕采集"""
+ self.stop_flag = True
+
+# -------------------------- 3. 系统音频采集轨道 --------------------------
+class SystemAudioTrack(MediaStreamTrack):
+ kind = "audio"
+
+ def __init__(self, sample_rate=48000, channels=2):
+ super().__init__()
+ self.sample_rate = sample_rate
+ self.channels = channels
+ self.stop_flag = False
+ self.pts = 0
+ self.audio_queue = asyncio.Queue(maxsize=10) # 音频数据队列
+
+ # 启动麦克风/系统音频采集(这里默认采集麦克风,系统音频见备注)
+ self._start_audio_capture()
+
+ def _start_audio_capture(self):
+ """启动音频采集(麦克风)"""
+ def audio_callback(indata, frames, time, status):
+ if status or self.stop_flag:
+ return
+ # 将音频数据转为float32格式(aiortc要求)
+ audio_data = indata.astype(np.float32)
+ self.audio_queue.put_nowait(audio_data)
+
+ # 打开音频输入流
+ self.audio_stream = sd.InputStream(
+ samplerate=self.sample_rate,
+ channels=self.channels,
+ callback=audio_callback,
+ blocksize=1024
+ )
+ self.audio_stream.start()
+
+ async def recv(self):
+ """aiortc核心方法:持续返回音频帧"""
+ if self.stop_flag and self.audio_queue.empty():
+ raise StopAsyncIteration
+
+ # 从队列获取音频数据
+ audio_data = await self.audio_queue.get()
+ sample_count = len(audio_data)
+
+ # 转换为aiortc AudioFrame
+ audio_frame = AudioFrame(
+ samples=audio_data.T, # 转置为 (channels, samples)
+ sample_rate=self.sample_rate,
+ channels=self.channels
+ )
+ audio_frame.pts = int(self.pts)
+ audio_frame.time_base = np.array([1, self.sample_rate])
+ self.pts += sample_count
+
+ return audio_frame
+
+ def stop(self):
+ """停止音频采集"""
+ self.stop_flag = True
+ self.audio_stream.stop()
+ self.audio_stream.close()
+
+# -------------------------- 2. 工具函数:枚举音频设备(解决Error querying device -1错误) --------------------------
+def list_audio_devices():
+ """枚举所有音频设备,输出ID和名称,方便手动选择"""
+ print("📢 可用音频设备列表:")
+ devices = sd.query_devices()
+ for idx, dev in enumerate(devices):
+ print(f"ID {idx}: {dev['name']} | 输入通道:{dev['max_input_channels']} | 输出通道:{dev['max_output_channels']}")
+ return devices
+
+def get_vb_cable_device_id():
+ """查找VB-Cable虚拟音频设备ID(系统声音采集必需)"""
+ devices = sd.query_devices()
+ for idx, dev in enumerate(devices):
+ if "Cable" in dev['name'] and dev['max_input_channels'] > 0:
+ return idx
+ raise Exception("❌ 未找到VB-Cable虚拟音频设备!\n 请先安装:https://vb-audio.com/Cable/")
+
+# -------------------------- 3. 系统声音录制为MP3 --------------------------
+def record_system_audio_to_mp3(output_mp3="system_audio.mp3", duration=5, sample_rate=48000):
+ """
+ 录制系统声音为MP3(需先安装VB-Cable并设置系统音频输出到VB-Cable)
+ :param output_mp3: 输出MP3文件名
+ :param duration: 录制时长(秒)
+ :param sample_rate: 采样率
+ """
+ # 修复device -1错误:手动指定VB-Cable设备ID
+ vb_cable_id = get_vb_cable_device_id()
+ print(f"✅ 已找到VB-Cable设备,ID:{vb_cable_id}")
+
+ # 1. 录制音频为WAV(先录WAV再转MP3,避免编码问题)
+ wav_file = "temp_audio.wav"
+ print(f"🎙️ 开始录制系统声音({duration}秒)...")
+ audio_data = sd.rec(
+ int(duration * sample_rate),
+ samplerate=sample_rate,
+ channels=2,
+ dtype='float32',
+ device=vb_cable_id # 关键:指定有效设备ID,解决-1错误
+ )
+ sd.wait() # 等待录制完成
+
+ # 2. 保存为WAV(临时文件)
+ with wave.open(wav_file, 'wb') as wf:
+ wf.setnchannels(2)
+ wf.setsampwidth(2)
+ wf.setframerate(sample_rate)
+ wf.writeframes((audio_data * 32767).astype(np.int16).tobytes())
+
+ # 3. WAV转MP3(用OpenCV/ffmpeg,无需额外依赖)
+ print("🔄 转换WAV到MP3...")
+ os.system(f"ffmpeg -y -i {wav_file} -codec:a libmp3lame -b:a 192k {output_mp3}")
+
+ # 4. 删除临时WAV文件
+ if os.path.exists(wav_file):
+ os.remove(wav_file)
+
+ print(f"✅ 系统声音已保存:{output_mp3}")
+
+# -------------------------- 4. 屏幕60帧/秒截图(5秒)并合成MP4 --------------------------
+def capture_screen_60fps(output_dir="screen_shots", duration=5, fps=60):
+ """
+ 5秒内每秒60张屏幕截图(共300张),并合成高帧率MP4
+ :param output_dir: 截图保存目录
+ :param duration: 录制时长(秒)
+ :param fps: 帧率(60帧/秒)
+ """
+ # 1. 创建截图目录
+ if not os.path.exists(output_dir):
+ os.makedirs(output_dir)
+ print(f"🖥️ 开始屏幕截图({duration}秒,{fps}帧/秒)...")
+
+ # 2. 获取屏幕真实分辨率(适配高DPI)
+ user32 = ctypes.windll.user32
+ screen_width = user32.GetSystemMetrics(0)
+ screen_height = user32.GetSystemMetrics(1)
+ total_frames = duration * fps # 总帧数:5*60=300帧
+ frame_interval = 1.0 / fps # 每帧间隔(约16.67ms)
+
+ # 3. 循环截图(60帧/秒)
+ for frame_idx in range(total_frames):
+ # Windows原生API采集屏幕(低延迟,适配60帧)
+ hdesktop = win32gui.GetDesktopWindow()
+ hwnd_dc = win32gui.GetWindowDC(hdesktop)
+ mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc)
+ save_dc = mfc_dc.CreateCompatibleDC()
+
+ save_bitmap = win32ui.CreateBitmap()
+ save_bitmap.CreateCompatibleBitmap(mfc_dc, screen_width, screen_height)
+ save_dc.SelectObject(save_bitmap)
+ save_dc.BitBlt((0, 0), (screen_width, screen_height), mfc_dc, (0, 0), win32con.SRCCOPY)
+
+ # 转换为numpy数组并保存为PNG(无损)
+ bmp_data = save_bitmap.GetBitmapBits(True)
+ frame = np.frombuffer(bmp_data, dtype=np.uint8).reshape((screen_height, screen_width, 4))
+ frame = frame[:, :, :3][:, :, ::-1] # BGRA→BGR
+
+ # 保存截图(命名:frame_0001.png ~ frame_0300.png)
+ frame_filename = os.path.join(output_dir, f"frame_{frame_idx+1:04d}.png")
+ cv2.imwrite(frame_filename, frame, [cv2.IMWRITE_PNG_COMPRESSION, 0])
+
+ # 释放资源
+ win32gui.DeleteObject(save_bitmap.GetHandle())
+ save_dc.DeleteDC()
+ mfc_dc.DeleteDC()
+ win32gui.ReleaseDC(hdesktop, hwnd_dc)
+
+ # 控制帧率(确保每秒60帧)
+ asyncio.run(
+ asyncio.sleep(frame_interval)
+ )
+
+ # 进度提示
+ if (frame_idx + 1) % 60 == 0:
+ print(f" 已截图:{frame_idx+1}/{total_frames}帧({int((frame_idx+1)/total_frames*100)}%)")
+
+ # 4. 将截图合成60帧/秒的MP4
+ print("🔄 将截图合成60帧/秒MP4...")
+ output_mp4 = "screen_60fps.mp4"
+ # FFmpeg合成命令(H.264编码,60帧/秒)
+ ffmpeg_cmd = (
+ f'ffmpeg -y -framerate {fps} -i {output_dir}/frame_%04d.png '
+ f'-c:v libx264 -r {fps} -pix_fmt yuv420p {output_mp4}'
+ )
+ os.system(ffmpeg_cmd)
+
+ print(f"✅ 60帧屏幕视频已保存:{output_mp4}")
+ print(f"📁 原始截图保存在:{output_dir}")
+
+# -------------------------- 4. 主逻辑:采集并保存为MP4 --------------------------
+async def record_screen_to_mp4(output_file="screen_record.mp4", duration=10):
+ """
+ 录制屏幕+音频到MP4
+ :param output_file: 输出MP4文件名
+ :param duration: 录制时长(秒)
+ """
+ # 1. 创建音视频轨道
+ video_track = ScreenVideoTrack(fps=30, scale_factor=0.8) # 0.8倍缩放,减小文件
+ audio_track = SystemAudioTrack(sample_rate=48000)
+
+ # 2. 创建MediaRecorder(封装为MP4)
+ recorder = MediaRecorder(
+ output_file,
+ format="mp4", # 指定输出格式
+ options={
+ "video_codec": "h264", # H.264编码,兼容性最好
+ "audio_codec": "aac", # AAC音频编码
+ "video_bitrate": "2000k" # 视频码率(可调整)
+ }
+ )
+
+ # 3. 添加音视频轨道到录制器
+ recorder.addTrack(video_track)
+ recorder.addTrack(audio_track)
+
+ # 4. 开始录制
+ print(f"✅ 开始录制屏幕,时长{duration}秒,输出文件:{output_file}")
+ await recorder.start()
+
+ # 5. 录制指定时长
+ await asyncio.sleep(duration)
+
+ # 6. 停止录制并清理资源
+ print("🔚 录制结束,正在保存文件...")
+ video_track.stop()
+ audio_track.stop()
+ await recorder.stop()
+
+ print(f"✅ 文件已保存:{output_file}")
+
+# -------------------------- 5. 运行入口 --------------------------
+if __name__ == "__main__":
+ import cv2 # 延迟导入,避免启动报错
+
+ # 列出所有 音频设备
+ all_audio_devices = list_audio_devices()
+ # 运行录制(录制10秒,输出screen_record.mp4)
+ try:
+ # asyncio.run(record_screen_to_mp4(output_file="screen_record.mp4", duration=10))
+ # 第二步:录制系统声音为MP3(5秒)
+ if all_audio_devices:
+ record_system_audio_to_mp3(output_mp3="system_audio.mp3", duration=5)
+
+ # 第三步:屏幕60帧/秒截图(5秒)并合成MP4
+ capture_screen_60fps(output_dir="screen_shots", duration=5, fps=60)
+
+ pass
+
+ except KeyboardInterrupt:
+ print("\n🛑 录制被手动终止")
+ except Exception as e:
+ print(f"❌ 录制出错:{str(e)}")
+ print("💡 排查步骤:")
+ print(" 1. 确认FFmpeg已添加到系统PATH")
+ print(" 2. 确认依赖包已安装:pip install -r requirements.txt")
+ print(" 3. 以管理员身份运行脚本")
+
+# -------------------------- 备注:系统音频采集(进阶) --------------------------
+# 如需采集系统播放的音频(而非麦克风),需替换SystemAudioTrack为以下逻辑:
+# 1. 安装虚拟音频线(VB-Cable):https://vb-audio.com/Cable/
+# 2. 将系统音频输出设置为VB-Cable
+# 3. 将SystemAudioTrack的设备ID指定为VB-Cable的输入设备ID
+# 示例:
+# def _start_audio_capture(self):
+# # 列出所有音频设备,找到VB-Cable的ID
+# devices = sd.query_devices()
+# cable_id = None
+# for idx, dev in enumerate(devices):
+# if "Cable" in dev['name'] and dev['max_input_channels'] > 0:
+# cable_id = idx
+# break
+# if cable_id is None:
+# raise Exception("未找到VB-Cable虚拟音频设备")
+# # 用VB-Cable ID启动采集
+# self.audio_stream = sd.InputStream(
+# device=cable_id,
+# samplerate=self.sample_rate,
+# channels=self.channels,
+# callback=self.audio_callback,
+# blocksize=1024
+# )
+# self.audio_stream.start()
\ No newline at end of file
diff --git a/myRtc.py b/myRtc.py
new file mode 100644
index 0000000..59993fa
--- /dev/null
+++ b/myRtc.py
@@ -0,0 +1,373 @@
+'''
+通过python获取win屏幕媒体流传递给js
+'''
+import asyncio
+import ctypes
+import threading
+import numpy as np
+import win32gui
+import win32ui
+import win32con
+import webview
+from aiortc import MediaStreamTrack, RTCPeerConnection
+from aiortc import MediaStreamTrack, RTCPeerConnection, RTCSessionDescription
+
+# -------------------------- 1. Windows高DPI适配 --------------------------
+ctypes.windll.shcore.SetProcessDpiAwareness(2)
+
+# -------------------------- 2. 兼容VideoFrame导入 --------------------------
+try:from aiortc.contrib.media import VideoFrame,AudioFrame
+except ImportError:from aiortc import VideoFrame,AudioFrame
+
+# -------------------------- 2. AIORTC屏幕采集轨道 --------------------------
+class ScreenVideoTrack(MediaStreamTrack):
+ kind = "video"
+
+ def __init__(self, fps=30):
+ super().__init__()
+ self.fps = fps
+ self.stop_flag = False
+ self.pts = 0
+ self.frame_interval = 1.0 / fps
+ # 获取真实屏幕分辨率(适配高DPI)
+ self.user32 = ctypes.windll.user32
+ self.width = self.user32.GetSystemMetrics(0)
+ self.height = self.user32.GetSystemMetrics(1)
+
+ def capture_screen(self):
+ """Windows原生GDI采集屏幕(低延迟)"""
+ hdesktop = win32gui.GetDesktopWindow()
+ hwnd_dc = win32gui.GetWindowDC(hdesktop)
+ mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc)
+ save_dc = mfc_dc.CreateCompatibleDC()
+ save_bitmap = win32ui.CreateBitmap()
+ save_bitmap.CreateCompatibleBitmap(mfc_dc, self.width, self.height)
+ save_dc.SelectObject(save_bitmap)
+ save_dc.BitBlt((0, 0), (self.width, self.height), mfc_dc, (0, 0), win32con.SRCCOPY)
+
+ # 转换为numpy数组(BGR格式,适配aiortc)
+ bmp_data = save_bitmap.GetBitmapBits(True)
+ frame = np.frombuffer(bmp_data, dtype=np.uint8).reshape((self.height, self.width, 4))
+ frame = frame[:, :, :3][:, :, ::-1] # BGRA → BGR
+
+ # 释放资源,避免内存泄漏
+ win32gui.DeleteObject(save_bitmap.GetHandle())
+ save_dc.DeleteDC()
+ mfc_dc.DeleteDC()
+ win32gui.ReleaseDC(hdesktop, hwnd_dc)
+ return frame
+
+ async def recv(self):
+ """aiortc核心方法:返回视频帧"""
+ if self.stop_flag:
+ raise StopAsyncIteration
+ await asyncio.sleep(self.frame_interval)
+ frame_data = self.capture_screen()
+
+ # 转换为aiortc VideoFrame
+ video_frame = VideoFrame.from_ndarray(frame_data, format="bgr24")
+ video_frame.pts = int(self.pts)
+ video_frame.time_base = np.array([1, self.fps])
+ self.pts += 1
+ return video_frame
+
+ def stop(self):
+ """停止屏幕采集"""
+ self.stop_flag = True
+
+# -------------------------- 3. WebRTC服务(异步线程运行) --------------------------
+class WebRTCService:
+ def __init__(self):
+ self.pc = None
+ self.screen_track = None
+ self.loop = asyncio.new_event_loop()
+ self.thread = None
+
+ def start_async_loop(self):
+ """在子线程启动asyncio循环,避免阻塞pywebview UI"""
+ asyncio.set_event_loop(self.loop)
+ self.loop.run_forever()
+
+ async def create_offer(self):
+ """生成Offer信令,供前端调用"""
+ # 初始化PeerConnection(配置STUN服务器,解决NAT穿透)
+ self.pc = RTCPeerConnection()
+ self.pc.addIceServers([{"urls": "stun:stun.l.google.com:19302"}])
+
+ # 添加屏幕采集轨道
+ self.screen_track = ScreenVideoTrack(fps=30)
+ self.pc.addTrack(self.screen_track)
+ print("✅ Python侧:已添加屏幕采集轨道")
+
+ # 监听连接状态
+ @self.pc.on("connectionstatechange")
+ def on_connectionstatechange():
+ print(f"🔌 WebRTC连接状态:{self.pc.connectionState}")
+ if self.pc.connectionState in ["failed", "closed", "disconnected"]:
+ if self.screen_track:
+ self.screen_track.stop()
+
+ # 生成Offer并设置本地SDP
+ offer = await self.pc.createOffer()
+ await self.pc.setLocalDescription(offer)
+
+ # 返回序列化的Offer(供前端解析)
+ return {
+ "sdp": self.pc.localDescription.sdp,
+ "type": self.pc.localDescription.type
+ }
+
+ async def set_answer(self, answer_data):
+ """接收前端的Answer信令并应用"""
+ if not self.pc:
+ raise Exception("PeerConnection未初始化")
+
+ # 反序列化Answer
+ answer = RTCSessionDescription(
+ sdp=answer_data["sdp"],
+ type=answer_data["type"]
+ )
+ await self.pc.setRemoteDescription(answer)
+ print("✅ Python侧:已应用前端Answer信令,P2P连接建立")
+
+ def start(self):
+ """启动WebRTC服务(子线程)"""
+ self.thread = threading.Thread(target=self.start_async_loop, daemon=True)
+ self.thread.start()
+
+ def stop(self):
+ """停止服务并清理资源"""
+ if self.screen_track:
+ self.screen_track.stop()
+ if self.pc:
+ asyncio.run_coroutine_threadsafe(self.pc.close(), self.loop)
+ self.loop.stop()
+ if self.thread:
+ self.thread.join()
+
+# -------------------------- 4. PyWebView API通信层 --------------------------
+class PyWebViewAPI:
+ def __init__(self, webrtc_service):
+ self.webrtc_service = webrtc_service
+
+ # 供前端JS调用:获取Offer信令
+ async def get_offer(self):
+ return await asyncio.wrap_future(
+ asyncio.run_coroutine_threadsafe(self.webrtc_service.create_offer(), self.webrtc_service.loop)
+ )
+
+ # 供前端JS调用:发送Answer信令到Python
+ async def send_answer(self, answer_data):
+ await asyncio.wrap_future(
+ asyncio.run_coroutine_threadsafe(self.webrtc_service.set_answer(answer_data), self.webrtc_service.loop)
+ )
+
+# -------------------------- 5. 前端页面(向日葵风格) --------------------------
+HTML_CONTENT = """
+
+
+
+
+
+ Windows屏幕共享
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"""
+
+# -------------------------- 6. 主程序入口 --------------------------
+if __name__ == "__main__":
+ # 初始化WebRTC服务并启动异步线程
+ webrtc_service = WebRTCService()
+ webrtc_service.start()
+
+ # 初始化PyWebView API
+ api = PyWebViewAPI(webrtc_service)
+
+ # 创建PyWebView窗口(必须用Edge Chromium引擎,支持WebRTC)
+ window = webview.create_window(
+ title="Windows屏幕共享",
+ html=HTML_CONTENT,
+ width=1400,
+ height=900,
+ resizable=True,
+ confirm_close=True,
+ easy_drag=False,
+ js_api=api
+ )
+
+ # 启动PyWebView(关键:指定edgechromium引擎)
+ try:
+ webview.start(
+ gui='edgechromium', # 强制使用WebView2引擎
+ private_mode=False, # WebRTC必需关闭私有模式
+ )
+ except KeyboardInterrupt:
+ print("\n🛑 程序被手动终止")
+ finally:
+ # 清理资源
+ webrtc_service.stop()
+ print("✅ 资源已清理完成")
\ No newline at end of file
diff --git a/py.code-workspace b/py.code-workspace
new file mode 100644
index 0000000..cf6a6c3
--- /dev/null
+++ b/py.code-workspace
@@ -0,0 +1,24 @@
+{
+ "folders": [
+ {
+ "path": ".",
+ },
+ ],
+ "settings": {
+ "files.autoSave": "onFocusChange",
+ "editor.wordWrap": "on",
+ "[python]": {
+ "diffEditor.ignoreTrimWhitespace": false,
+ "editor.formatOnType": true,
+ "editor.wordBasedSuggestions": "off",
+ },
+ "git.ignoreLimitWarning": true,
+ "editor.fontSize": 12,
+ "editor.formatOnSave": true,
+ // "python.defaultInterpreterPath": "C:/v_floder/_wPy64/winpy312/python/python.exe",
+ // "python.defaultInterpreterPath": "E:/vFiles/.WPython64/wpy312/python/python.exe",
+ // "python.defaultInterpreterPath": "D:/vbox_files/.wpy64/wpy312/python/python.exe",
+ "python.envFile": "${workspaceFolder}/.venv",
+ "editor.minimap.enabled": true,
+ },
+}
diff --git a/py.ipynb b/py.ipynb
new file mode 100644
index 0000000..8fffa57
--- /dev/null
+++ b/py.ipynb
@@ -0,0 +1,44 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "86b87380",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "1\n"
+ ]
+ }
+ ],
+ "source": [
+ "if __name__ == \"__main__\":\n",
+ " print(1)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": ".venv",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.12.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/py2exe.py b/py2exe.py
new file mode 100644
index 0000000..2fbad27
--- /dev/null
+++ b/py2exe.py
@@ -0,0 +1,46 @@
+
+import time
+
+import os
+from PyInstaller import __main__ as pyi
+
+def create_executable(script, output_name, icon, data_files,module_args_files):
+ data_args = []
+ for src, dest in data_files:
+ sep = ';' if os.name == 'nt' else ':' # 根据操作系统选择分隔符
+ data_args.extend(['--add-data', f'{src}{sep}{dest}'])
+
+ module_args = []
+ for moduls_file in module_args_files:
+ module_args.extend(['--hidden-import', f'{moduls_file}'])
+
+ # module_args.extend(['excludes', f'PyQt5'])
+
+ pyi.run([
+ *data_args, # 包含的静态文件
+ *module_args, #包含的库
+ '--onefile', #将所有依赖打包为单个可执行文件
+ # '--onedir', #打包为文件夹(默认方式),包含 exe + 依赖库 / 文件夹
+ '--clean',
+ # '--windowed', # 隐藏控制台窗口(GUI 应用必备)
+ '--console', # 显示控制台窗口
+ # f"--exclude-module=PyQt5", # 排除指定库
+ f'--icon={icon}', # 设置程序icon
+ f'--name={output_name}',
+ script,
+ ])
+
+CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
+if __name__ == "__main__":
+
+ # 示例数据文件列表
+ data_files = [
+ ("static", "static"),
+ ("templates", "templates"),
+ ("assets", "assets"),
+ ("module", "module"),
+ ]
+ import_modules = [
+ "pystray","pillow","pywebview","aiortc","opencv-python","numpy","mss"
+ ]
+ create_executable(f"app.py", f"fooDesk_{int(time.time())}", "icon.ico", data_files,import_modules)
diff --git a/screenCaptureNoConfirm.py b/screenCaptureNoConfirm.py
new file mode 100644
index 0000000..4197c75
--- /dev/null
+++ b/screenCaptureNoConfirm.py
@@ -0,0 +1,148 @@
+"""screenCaptureNoConfirm
+pywebview 通过js跳过确认直接获取屏幕媒体流
+"""
+
+import webview
+import threading
+import time
+
+# -------------------------- 1. PyWebView配置 --------------------------
+class ScreenCaptureAPI:
+ def __init__(self):
+ self.window = None
+
+ def set_window(self, window):
+ self.window = window
+
+ def auto_trigger_capture(self):
+ """后台自动触发屏幕采集(模拟用户点击)"""
+ # 延迟1秒(等待页面加载完成)
+ time.sleep(1)
+ # 执行JS自动点击按钮,触发屏幕采集
+ self.window.evaluate_js("document.getElementById('autoCaptureBtn').click();")
+
+# -------------------------- 2. 前端HTML(核心:自动触发+无感采集) --------------------------
+HTML_CONTENT = """
+
+
+
+
+ 自动获取屏幕流
+
+
+
+
+
+
+ 状态:等待自动采集...
+
+
+
+
+
+
+
+"""
+
+# -------------------------- 3. 主程序入口 --------------------------
+if __name__ == "__main__":
+ # 初始化API
+ api = ScreenCaptureAPI()
+
+ # 创建pywebview窗口(关键配置:WebView2引擎+权限豁免)
+ window = webview.create_window(
+ title="自动获取屏幕流",
+ html=HTML_CONTENT,
+ width=1400,
+ height=900,
+ resizable=True,
+ # confirm_close=True,
+ # 额外权限配置(WebView2)
+ # webview_settings={
+ # "web_security": False, # 关闭跨域限制(本地运行需开启)
+ # "allow_displaying_insecure_content": True,
+ # "allow_running_insecure_content": True
+ # },
+ js_api=api
+ )
+
+ api.set_window(window)
+
+ # 定义页面加载完成后的回调(自动触发采集)
+ def on_page_loaded():
+ # 启动线程自动触发点击
+ trigger_thread = threading.Thread(target=api.auto_trigger_capture, daemon=True)
+ trigger_thread.start()
+
+ # 暴露API给前端
+ api.page_loaded = on_page_loaded
+
+ # 启动pywebview
+ try:
+ webview.start(
+ private_mode=False, # 关键:关闭私有模式,授予本地权限
+ debug=True,
+ http_server=True
+ )
+ except KeyboardInterrupt:
+ print("程序已终止")
\ No newline at end of file
diff --git a/static/dexieParse.js b/static/dexieParse.js
new file mode 100644
index 0000000..1c01cb9
--- /dev/null
+++ b/static/dexieParse.js
@@ -0,0 +1,49 @@
+// dexie文档:https://dexie.org/docs/API-Reference https://www.npmrc.cn/en/Dexie-js.html
+class dexieDemo {
+ dbName = "jafar";
+ dbVersion = 1;
+ constructor(dbName, dbVersion) {
+ if (dbName) {
+ this.dbName = dbName;
+ }
+ if (dbVersion) {
+ this.dbVersion = dbVersion;
+ }
+ this.db = new Dexie(this.dbName);
+ this.db.version(this.dbVersion).stores(
+ { tabName: "++id,nKey,nValue,nTime" }, // 创建表,已有表则建立链接,id为自增主键;仅需要列举需要索引的字段即可
+ );
+ }
+ async get() {
+ const getLastData = await this.db.tabName.orderBy("id").last();
+ return getLastData;
+ }
+ async post(nKey, nValue) {
+ await this.db.tabName.add({
+ nKey: nKey,
+ nValue: nValue,
+ nTime: Date.now(),
+ });
+ const getLastData = await this.db.tabName.orderBy("id").last();
+ }
+ async put(id) {
+ await this.db.tabName.put({
+ id: 1,
+ userName: "zhangsan",
+ });
+ }
+ async del(id) {
+ await this.db.tabName.delete(id);
+ }
+}
+
+export const myDexie = new dexieDemo();
+
+// document.querySelector("#goto_indexedDB_post").addEventListener("click",async () => {
+// let nKey = Math.random()
+// let nValue = new Date().getTime();
+// await myDexieDemo.post( nKey,nValue );
+// })
+// document.querySelector("#goto_indexedDB_get").addEventListener("click",async () => {
+// console.log( await myDexieDemo.get() )
+// })
diff --git a/static/favicon.ico b/static/favicon.ico
new file mode 100644
index 0000000..5fe1ce2
Binary files /dev/null and b/static/favicon.ico differ
diff --git a/static/script.js b/static/script.js
new file mode 100644
index 0000000..3748402
--- /dev/null
+++ b/static/script.js
@@ -0,0 +1,18 @@
+// addEventListener 能添加事件
+// 鼠标事件 : 如 click(点击)、dblclick(双击)、mousedown(按下鼠标键)、mouseup(释放鼠标键)、mousemove(鼠标移动)、mouseenter(鼠标进入元素)、mouseleave(鼠标离开元素)、mouseover(鼠标悬停)和 mouseout(鼠标移出)。
+// 键盘事件 : 如 keydown(按下键盘键)、keyup(释放键盘键)和 keypress(按下字符键,已废弃)。
+// 触摸与指针事件 : 如 touchstart(触摸开始)、touchmove(触摸移动)、touchend(触摸结束)、pointerdown(指针按下)和 pointerup(指针释放),适用于触屏设备和笔输入。
+// 表单事件 : 如 submit(表单提交)、change(值改变)、input(输入内容)、focus(获得焦点)和 blur(失去焦点)。
+// 窗口与页面事件 : 如 load(页面加载完成)、resize(窗口大小改变)、scroll(滚动)、beforeunload(页面即将卸载)和 unload(页面卸载)。
+// 媒体事件 : 如 play(媒体开始播放)、pause(媒体暂停)、ended(媒体播放结束)和 loadeddata(媒体数据加载完成)。
+// 自定义事件 : 可以通过 Event 构造函数创建并派发自定义事件,用于应用内部组件通信。
+
+// 监听DOM加载完成事件
+document.addEventListener("DOMContentLoaded", function () {
+ console.log("document加载完成!!!");
+});
+
+document.querySelector("#appChildEleButton").addEventListener("click", () => {
+ const resultDiv = document.getElementById("appChildEle");
+ resultDiv.innerHTML += `点击添加元素完成`;
+});
diff --git a/static/styles.css b/static/styles.css
new file mode 100644
index 0000000..1b40f9b
--- /dev/null
+++ b/static/styles.css
@@ -0,0 +1,39 @@
+.myFontWenDao {
+ font-family: myFontWenDao, sans-serif;
+}
+
+#app {
+ border: 1px dotted blueviolet;
+ border-radius: 3rem;
+ padding: 2rem;
+ margin: 1rem;
+}
+
+#app .title {
+ text-align: center;
+}
+#appChildEleButton {
+ background-color: whitesmoke;
+ border: 2px dotted grey;
+ border-radius: 0.5rem;
+}
+#appChildEleButton :hover {
+ opacity: 0.5;
+ background-color: black;
+}
+
+.con {
+ padding: 0.5rem;
+ margin: 1rem auto;
+ border: 1px dotted grey;
+ border-radius: 1rem;
+ text-align: center;
+ width: fit-content;
+}
+li {
+ display: block;
+}
+
+#localVideo {
+ display: none;
+}
diff --git a/templates/app_JsToPy.html b/templates/app_JsToPy.html
new file mode 100644
index 0000000..efdc467
--- /dev/null
+++ b/templates/app_JsToPy.html
@@ -0,0 +1,111 @@
+
+
+
+
+ PyWebView 双向通信
+
+
+
+
+
+
Python ↔ JS 双向通信
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/app_JsToPy.py b/templates/app_JsToPy.py
new file mode 100644
index 0000000..9be0147
--- /dev/null
+++ b/templates/app_JsToPy.py
@@ -0,0 +1,76 @@
+import webview
+import threading
+import time,os
+
+CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
+
+# ---------------------- 1. 定义供 JS 调用的 Python 函数 ----------------------
+class Api:
+ """暴露给 JS 的 Python 接口类(所有方法都会被 JS 访问)"""
+ def __init__(self):
+ self.window = None # 保存窗口实例,用于 Python 主动调用 JS
+
+ def set_window(self, window):
+ """初始化时绑定窗口实例"""
+ self.window = window
+
+ def python_hello(self, name):
+ """JS 调用的同步函数:接收参数,返回结果"""
+ print(f"[Python] 收到 JS 调用,参数:{name}")
+ return f"Hello {name}! 这是 Python 返回的消息"
+
+ def python_async_task(self, duration):
+ """JS 调用的异步函数:模拟耗时任务(不阻塞前端)"""
+ def async_task():
+ print(f"[Python] 开始异步任务,耗时 {duration} 秒")
+ time.sleep(int(duration))
+ # 异步任务完成后,主动调用 JS 函数通知结果
+ self.window.evaluate_js(f'js_receive_python_msg("异步任务完成!耗时 {duration} 秒")')
+ print(f"[Python] 异步任务结束")
+
+ # 启动子线程执行异步任务,避免阻塞 UI
+ threading.Thread(target=async_task, daemon=True).start()
+ return "异步任务已启动,请等待结果通知"
+
+ def python_close_window(self):
+ """JS 调用关闭窗口"""
+ print("[Python] 收到关闭窗口请求")
+ self.window.destroy()
+
+# ---------------------- 2. Python 主动调用 JS 函数 ----------------------
+def python_call_js_periodically(window):
+ """Python 定时调用 JS 函数(模拟主动推送数据)"""
+ count = 0
+ while window.is_alive():
+ count += 1
+ # 执行 JS 函数,传递参数
+ window.evaluate_js(f'js_receive_python_msg("Python 主动推送:第 {count} 条消息")')
+ time.sleep(3) # 每 3 秒推送一次
+ if count >= 5:
+ break
+
+# ---------------------- 3. 启动窗口 ----------------------
+if __name__ == "__main__":
+ # 创建 API 实例
+ api = Api()
+
+ # 加载本地 HTML 文件(也可加载远程 URL:url="https://xxx.com")
+ window = webview.create_window(
+ title="PyWebView 双向通信示例",
+ url=f"file:///{CURRENT_DIR}/templates/app_JsToPy.html",
+ resizable=True,
+ js_api=api # 关键:暴露 Python API 给 JS
+ )
+
+ # 绑定窗口实例到 API(供异步任务调用 JS)
+ api.set_window(window)
+
+ # 启动 Python 主动调用 JS 的线程(非阻塞)
+ t = threading.Thread(target=python_call_js_periodically, args=(window,), daemon=True)
+ t.start()
+
+ # 运行窗口(阻塞主线程)
+ webview.start(
+ debug=True, # 开发环境开启调试,生产环境关闭
+ http_server=True # 启用内置 HTTP 服务器(加载本地 HTML 必需)
+ )
\ No newline at end of file