jl-db-comp 0.1.0__py3-none-any.whl → 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jl_db_comp/_version.py +1 -1
- jl_db_comp/connections.py +173 -0
- jl_db_comp/routes.py +353 -7
- jl_db_comp/tests/test_routes.py +1 -1
- {jl_db_comp/labextension → jl_db_comp-0.1.1.data/data/share/jupyter/labextensions/jl_db_comp}/package.json +3 -2
- {jl_db_comp-0.1.0.data → jl_db_comp-0.1.1.data}/data/share/jupyter/labextensions/jl_db_comp/schemas/jl_db_comp/package.json.orig +2 -1
- {jl_db_comp-0.1.0.data → jl_db_comp-0.1.1.data}/data/share/jupyter/labextensions/jl_db_comp/schemas/jl_db_comp/plugin.json +4 -4
- jl_db_comp-0.1.1.data/data/share/jupyter/labextensions/jl_db_comp/static/171.d366980651e0db8d978c.js +1 -0
- jl_db_comp-0.1.1.data/data/share/jupyter/labextensions/jl_db_comp/static/728.6552504d5b9b27551bc5.js +1 -0
- jl_db_comp-0.1.1.data/data/share/jupyter/labextensions/jl_db_comp/static/remoteEntry.48c365d3de0d860be537.js +1 -0
- jl_db_comp-0.1.1.data/data/share/jupyter/labextensions/jl_db_comp/static/third-party-licenses.json +16 -0
- {jl_db_comp-0.1.0.dist-info → jl_db_comp-0.1.1.dist-info}/METADATA +103 -1
- jl_db_comp-0.1.1.dist-info/RECORD +20 -0
- jl_db_comp/labextension/build_log.json +0 -728
- jl_db_comp/labextension/schemas/jl_db_comp/package.json.orig +0 -214
- jl_db_comp/labextension/schemas/jl_db_comp/plugin.json +0 -27
- jl_db_comp/labextension/static/lib_index_js.a0969ed73da70f2cc451.js +0 -561
- jl_db_comp/labextension/static/lib_index_js.a0969ed73da70f2cc451.js.map +0 -1
- jl_db_comp/labextension/static/remoteEntry.5763ae02737e035e938c.js +0 -560
- jl_db_comp/labextension/static/remoteEntry.5763ae02737e035e938c.js.map +0 -1
- jl_db_comp/labextension/static/style.js +0 -4
- jl_db_comp/labextension/static/style_index_js.5364c7419a6b9db5d727.js +0 -508
- jl_db_comp/labextension/static/style_index_js.5364c7419a6b9db5d727.js.map +0 -1
- jl_db_comp-0.1.0.data/data/share/jupyter/labextensions/jl_db_comp/build_log.json +0 -728
- jl_db_comp-0.1.0.data/data/share/jupyter/labextensions/jl_db_comp/package.json +0 -219
- jl_db_comp-0.1.0.data/data/share/jupyter/labextensions/jl_db_comp/static/lib_index_js.a0969ed73da70f2cc451.js +0 -561
- jl_db_comp-0.1.0.data/data/share/jupyter/labextensions/jl_db_comp/static/lib_index_js.a0969ed73da70f2cc451.js.map +0 -1
- jl_db_comp-0.1.0.data/data/share/jupyter/labextensions/jl_db_comp/static/remoteEntry.5763ae02737e035e938c.js +0 -560
- jl_db_comp-0.1.0.data/data/share/jupyter/labextensions/jl_db_comp/static/remoteEntry.5763ae02737e035e938c.js.map +0 -1
- jl_db_comp-0.1.0.data/data/share/jupyter/labextensions/jl_db_comp/static/style_index_js.5364c7419a6b9db5d727.js +0 -508
- jl_db_comp-0.1.0.data/data/share/jupyter/labextensions/jl_db_comp/static/style_index_js.5364c7419a6b9db5d727.js.map +0 -1
- jl_db_comp-0.1.0.dist-info/RECORD +0 -33
- {jl_db_comp-0.1.0.data → jl_db_comp-0.1.1.data}/data/etc/jupyter/jupyter_server_config.d/jl_db_comp.json +0 -0
- {jl_db_comp-0.1.0.data → jl_db_comp-0.1.1.data}/data/share/jupyter/labextensions/jl_db_comp/install.json +0 -0
- {jl_db_comp-0.1.0.data → jl_db_comp-0.1.1.data}/data/share/jupyter/labextensions/jl_db_comp/static/style.js +0 -0
- {jl_db_comp-0.1.0.dist-info → jl_db_comp-0.1.1.dist-info}/WHEEL +0 -0
- {jl_db_comp-0.1.0.dist-info → jl_db_comp-0.1.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"jupyter.lab.shortcuts": [],
|
|
3
3
|
"title": "PostgreSQL Database Completer",
|
|
4
|
-
"description": "Settings for PostgreSQL database autocomplete in JupyterLab.",
|
|
4
|
+
"description": "Settings for PostgreSQL database autocomplete in JupyterLab. Uses jupysql connections.ini for database configuration.",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"properties": {
|
|
7
7
|
"enabled": {
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
"description": "Enable or disable PostgreSQL table and column completions",
|
|
11
11
|
"default": true
|
|
12
12
|
},
|
|
13
|
-
"
|
|
13
|
+
"connectionName": {
|
|
14
14
|
"type": "string",
|
|
15
|
-
"title": "
|
|
16
|
-
"description": "
|
|
15
|
+
"title": "Connection Name",
|
|
16
|
+
"description": "Connection name from connections.ini file. Leave empty to use the active jupysql connection from the notebook kernel, or the first available connection.",
|
|
17
17
|
"default": ""
|
|
18
18
|
},
|
|
19
19
|
"schema": {
|
jl_db_comp-0.1.1.data/data/share/jupyter/labextensions/jl_db_comp/static/171.d366980651e0db8d978c.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";(self.webpackChunkjl_db_comp=self.webpackChunkjl_db_comp||[]).push([[171],{171(e,n,t){t.r(n),t.d(n,{default:()=>m});var o=t(206),s=t(428),i=t(490),a=t(830),r=t(469);async function c(e="",n={}){const t=a.ServerConnection.makeSettings(),o=r.URLExt.join(t.baseUrl,"jl-db-comp",e);let s;try{s=await a.ServerConnection.makeRequest(o,n,t)}catch(e){throw new a.ServerConnection.NetworkError(e)}let i=await s.text();if(i.length>0)try{i=JSON.parse(i)}catch(e){console.log("Not a JSON response body.",s)}if(!s.ok)throw new a.ServerConnection.ResponseError(s,i.message||i);return i}class l{constructor(e,n){this.identifier="jl_db_comp:postgres-completer",this.renderer=null,this._cache=new Map,this._cacheTTL=3e5,this._settings=null,this._notebookTracker=null,this._connectionName="",this._schema="public",this._enabled=!0,this._availableConnections=[],this._cachedKernelConfig=null,this._kernelConfigCacheTime=0,this._kernelConfigCacheTTL=3e4,this._sqlKeywords=["select","from","join","where","insert","update","delete","inner","left","right","outer","on","group","order","by","having","into","values","set"],this._notebookTracker=n||null,e&&(this._settings=e,this._loadSettings(),e.changed.connect(()=>{this._loadSettings()})),this._loadAvailableConnections()}_loadSettings(){this._settings&&(this._connectionName=this._settings.get("connectionName").composite,this._schema=this._settings.get("schema").composite,this._enabled=this._settings.get("enabled").composite)}async _loadAvailableConnections(){try{const e=await async function(){try{return await c("connections",{method:"GET"})}catch(e){if(e instanceof a.ServerConnection.ResponseError)console.error(`Failed to fetch connections: ${e.message}`);else{const n=e instanceof Error?e.message:"Unknown error";console.error(`Failed to fetch connections: ${n}`)}return{status:"error",connections:{},filePath:null,message:"Failed to fetch connections"}}}();"success"===e.status&&(this._availableConnections=Object.keys(e.connections))}catch(e){console.warn("Failed to load available connections:",e)}}async _getKernelConfig(){var e;const n=Date.now();if(this._cachedKernelConfig&&n-this._kernelConfigCacheTime<this._kernelConfigCacheTTL)return this._cachedKernelConfig;if(!this._notebookTracker)return null;const t=this._notebookTracker.currentWidget;if(!t)return null;const o=null===(e=t.sessionContext.session)||void 0===e?void 0:e.kernel;if(!o)return null;try{const e=o.requestExecute({code:"\nimport json\nimport os\nresult = {'connection': '', 'dsn_filename': ''}\n\n# Get active connection\ntry:\n from sql.connection import ConnectionManager\n conn = ConnectionManager.current\n if conn:\n for alias, c in ConnectionManager.connections.items():\n if c is conn:\n result['connection'] = alias\n break\nexcept:\n pass\n\n# Get dsn_filename from SqlMagic instance\ndsn_filename = None\ntry:\n from sql.magic import SqlMagic\n ip = get_ipython()\n if ip:\n for name, inst in ip.magics_manager.registry.items():\n if isinstance(inst, SqlMagic):\n dsn_filename = inst.dsn_filename\n break\nexcept:\n pass\n\n# Fallback: try to get from config\nif not dsn_filename:\n try:\n ip = get_ipython()\n if ip and hasattr(ip, 'config'):\n sql_config = ip.config.get('SqlMagic', {})\n if 'dsn_filename' in sql_config:\n dsn_filename = sql_config['dsn_filename']\n except:\n pass\n\n# Convert to absolute path if we have a dsn_filename\nif dsn_filename:\n if not os.path.isabs(dsn_filename):\n # Resolve relative to current working directory\n dsn_filename = os.path.abspath(dsn_filename)\n result['dsn_filename'] = dsn_filename\n\nprint(json.dumps(result))\n",silent:!0,store_history:!1}),t=await new Promise(n=>{let t="";e.onIOPub=e=>{if("stream"===e.header.msg_type){const n=e.content;"stdout"===n.name&&(t+=n.text)}},e.done.then(()=>{try{const e=JSON.parse(t.trim());n({connection:e.connection||"",dsnFilename:e.dsn_filename||""})}catch(e){n(null)}}).catch(()=>{n(null)})});return t&&(this._cachedKernelConfig=t,this._kernelConfigCacheTime=n),t}catch(e){return console.warn("Failed to get jupysql config from kernel:",e),null}}async isApplicable(e){if(!this._enabled)return!1;const n=e.editor;if(!n)return!1;const t=n.model.sharedModel.getSource();if(!t)return!1;const o=t.toLowerCase();return this._sqlKeywords.some(e=>o.includes(e))}async fetch(e,n){var t;if(!this._enabled)return{start:e.offset,end:e.offset,items:[]};const{text:o,offset:s}=e,i=this._extractContext(o,s);let r;if(i.jsonbColumn){const e=(null===(t=i.jsonbPath)||void 0===t?void 0:t.join("."))||"";r=`${i.schemaOrTable?`${i.schemaOrTable}.`:""}${i.jsonbColumn}->${e}.${i.prefix}`.toLowerCase()}else r=i.schema&&i.tableName?`${i.schema}.${i.tableName}.${i.prefix}`.toLowerCase():i.schemaOrTable?`${i.schemaOrTable}.${i.prefix}`.toLowerCase():i.prefix.toLowerCase();const l=this._getCached(r);if(l)return this._formatReply(l,e.offset,i.prefix);try{const n=await this._getKernelConfig();let t,o=this._connectionName;n&&(n.dsnFilename&&(t=n.dsnFilename),!o&&n.connection&&(o=n.connection)),!o&&this._availableConnections.length>0&&(o=this._availableConnections[0]);const s=await async function(e,n="",t="public",o,s,i,r,l){try{const a=new URLSearchParams;e&&a.append("connection",e),l&&a.append("connections_file",l),n&&a.append("prefix",n),a.append("schema",t),o&&a.append("table",o),s&&a.append("schema_or_table",s),i&&(a.append("jsonb_column",i),r&&r.length>0&&a.append("jsonb_path",JSON.stringify(r)));const h=`completions?${a.toString()}`,m=await c(h,{method:"GET"});return"error"===m.status?(console.error("PostgreSQL completion error:",m.message),[]):i&&m.jsonbKeys?m.jsonbKeys:o||s?m.columns.length>0?m.columns:m.tables:[...m.tables,...m.columns]}catch(e){if(e instanceof a.ServerConnection.ResponseError){const n=e.response.status;let t=e.message;"string"==typeof t&&(t.includes("<!DOCTYPE")||t.includes("<html"))&&(t=`HTML error page (${t.substring(0,100)}...)`),console.error(`PostgreSQL completions API failed (${n}): ${t}`)}else{const n=e instanceof Error?e.message:"Unknown error";console.error(`PostgreSQL completions API failed: ${n}`)}return[]}}(o||void 0,i.prefix,i.schema||this._schema,i.tableName,i.schemaOrTable,i.jsonbColumn,i.jsonbPath,t);return this._cache.set(r,{items:s,timestamp:Date.now()}),this._formatReply(s,e.offset,i.prefix)}catch(n){return console.error("Failed to fetch PostgreSQL completions:",n),{start:e.offset,end:e.offset,items:[]}}}_extractContext(e,n){const t=e.substring(0,n);if(t.includes("->")){const e=t.match(/([\w]+\.)?([\w]+)\s*->\s*(.*)$/);if(e){const n=e[1]?e[1].slice(0,-1):void 0,t=e[2],o=e[3],s=[],i=/['"]?([\w]+)['"]?\s*->/g;let a;for(;null!==(a=i.exec(o));)s.push(a[1]);const r=o.lastIndexOf("->");let c="";return c=r>=0?o.substring(r+2).trim().replace(/['"]/g,""):o.trim().replace(/['"]/g,""),{schemaOrTable:n,jsonbColumn:t,jsonbPath:s,prefix:c}}}const o=t.match(/([\w]+)\.([\w]+)\.([\w]*)$/);if(o)return{schema:o[1],tableName:o[2],prefix:o[3]};const s=t.match(/([\w]+)\.([\w]*)$/);if(s)return{schemaOrTable:s[1],prefix:s[2]};const i=t.match(/[\w]+$/),a=i?i[0]:"",r=e.toLowerCase().match(/\bfrom\s+([\w]+\.)?[\w]+/);if(r){const e=r[0].match(/\bfrom\s+(?:([\w]+)\.)?([\w]+)/);if(e){const n=e[1],t=e[2];return n?{schema:n,tableName:t,prefix:a}:{schemaOrTable:t,prefix:a}}}return{prefix:a}}_getCached(e){const n=e.toLowerCase(),t=this._cache.get(n);return t?Date.now()-t.timestamp>this._cacheTTL?(this._cache.delete(n),null):t.items:null}_formatReply(e,n,t){return{start:n-t.length,end:n,items:e.map(e=>{let n=e.name,t=e.name;"jsonb_key"===e.type&&(t=`'${e.name}'`),"column"===e.type&&e.table&&(n=`${e.name} (${e.table})`);let o,s="📊",i=e.name;return"table"===e.type?s="📋":"view"===e.type?s="👁️":"jsonb_key"===e.type&&(s="🔑",i=`0000${e.name}`),"column"===e.type&&e.dataType&&e.table?o=`${e.table}.${e.name}: ${e.dataType}`:"jsonb_key"===e.type&&e.keyPath&&(o=`JSONB key: ${e.keyPath.join(" -> ")}`),{label:`${s} ${n}`,insertText:t,sortText:i,type:e.type,documentation:o}})}}clearCache(){this._cache.clear()}}const h="jl_db_comp:plugin",m={id:h,description:"A JupyterLab extension to complete db queries in jupyterlab notebooks",autoStart:!0,requires:[o.ICompletionProviderManager],optional:[i.ISettingRegistry,s.INotebookTracker],activate:(e,n,t,o)=>{let s;t?t.load(h).then(e=>{s=new l(e,o),n.registerProvider(s),console.log("JupyterLab extension jl_db_comp is activated!")}).catch(e=>{console.error("Failed to load settings for jl_db_comp:",e),s=new l(null,o),n.registerProvider(s),console.log("JupyterLab extension jl_db_comp is activated!")}):(s=new l(null,o),n.registerProvider(s),console.log("JupyterLab extension jl_db_comp is activated!"))}}}}]);
|
jl_db_comp-0.1.1.data/data/share/jupyter/labextensions/jl_db_comp/static/728.6552504d5b9b27551bc5.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";(self.webpackChunkjl_db_comp=self.webpackChunkjl_db_comp||[]).push([[728],{56(e,n,t){e.exports=function(e){var n=t.nc;n&&e.setAttribute("nonce",n)}},72(e){var n=[];function t(e){for(var t=-1,r=0;r<n.length;r++)if(n[r].identifier===e){t=r;break}return t}function r(e,r){for(var a={},c=[],i=0;i<e.length;i++){var s=e[i],l=r.base?s[0]+r.base:s[0],u=a[l]||0,p="".concat(l," ").concat(u);a[l]=u+1;var f=t(p),d={css:s[1],media:s[2],sourceMap:s[3],supports:s[4],layer:s[5]};if(-1!==f)n[f].references++,n[f].updater(d);else{var m=o(d,r);r.byIndex=i,n.splice(i,0,{identifier:p,updater:m,references:1})}c.push(p)}return c}function o(e,n){var t=n.domAPI(n);return t.update(e),function(n){if(n){if(n.css===e.css&&n.media===e.media&&n.sourceMap===e.sourceMap&&n.supports===e.supports&&n.layer===e.layer)return;t.update(e=n)}else t.remove()}}e.exports=function(e,o){var a=r(e=e||[],o=o||{});return function(e){e=e||[];for(var c=0;c<a.length;c++){var i=t(a[c]);n[i].references--}for(var s=r(e,o),l=0;l<a.length;l++){var u=t(a[l]);0===n[u].references&&(n[u].updater(),n.splice(u,1))}a=s}}},113(e){e.exports=function(e,n){if(n.styleSheet)n.styleSheet.cssText=e;else{for(;n.firstChild;)n.removeChild(n.firstChild);n.appendChild(document.createTextNode(e))}}},314(e){e.exports=function(e){var n=[];return n.toString=function(){return this.map(function(n){var t="",r=void 0!==n[5];return n[4]&&(t+="@supports (".concat(n[4],") {")),n[2]&&(t+="@media ".concat(n[2]," {")),r&&(t+="@layer".concat(n[5].length>0?" ".concat(n[5]):""," {")),t+=e(n),r&&(t+="}"),n[2]&&(t+="}"),n[4]&&(t+="}"),t}).join("")},n.i=function(e,t,r,o,a){"string"==typeof e&&(e=[[null,e,void 0]]);var c={};if(r)for(var i=0;i<this.length;i++){var s=this[i][0];null!=s&&(c[s]=!0)}for(var l=0;l<e.length;l++){var u=[].concat(e[l]);r&&c[u[0]]||(void 0!==a&&(void 0===u[5]||(u[1]="@layer".concat(u[5].length>0?" ".concat(u[5]):""," {").concat(u[1],"}")),u[5]=a),t&&(u[2]?(u[1]="@media ".concat(u[2]," {").concat(u[1],"}"),u[2]=t):u[2]=t),o&&(u[4]?(u[1]="@supports (".concat(u[4],") {").concat(u[1],"}"),u[4]=o):u[4]="".concat(o)),n.push(u))}},n}},475(e,n,t){t.d(n,{A:()=>i});var r=t(601),o=t.n(r),a=t(314),c=t.n(a)()(o());c.push([e.id,"/*\n See the JupyterLab Developer Guide for useful CSS Patterns:\n\n https://jupyterlab.readthedocs.io/en/stable/developer/css.html\n*/\n\n/* PostgreSQL completion item styling */\n.jp-jl-db-comp-completion-item {\n font-family: var(--jp-code-font-family);\n}\n\n.jp-jl-db-comp-completion-table {\n color: var(--jp-ui-font-color1);\n}\n\n.jp-jl-db-comp-completion-column {\n color: var(--jp-brand-color1);\n}\n\n.jp-jl-db-comp-completion-documentation {\n font-size: var(--jp-code-font-size-small);\n color: var(--jp-ui-font-color2);\n font-style: italic;\n}\n",""]);const i=c},540(e){e.exports=function(e){var n=document.createElement("style");return e.setAttributes(n,e.attributes),e.insert(n,e.options),n}},601(e){e.exports=function(e){return e[1]}},659(e){var n={};e.exports=function(e,t){var r=function(e){if(void 0===n[e]){var t=document.querySelector(e);if(window.HTMLIFrameElement&&t instanceof window.HTMLIFrameElement)try{t=t.contentDocument.head}catch(e){t=null}n[e]=t}return n[e]}(e);if(!r)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");r.appendChild(t)}},728(e,n,t){var r=t(72),o=t.n(r),a=t(825),c=t.n(a),i=t(659),s=t.n(i),l=t(56),u=t.n(l),p=t(540),f=t.n(p),d=t(113),m=t.n(d),v=t(475),h={};h.styleTagTransform=m(),h.setAttributes=u(),h.insert=s().bind(null,"head"),h.domAPI=c(),h.insertStyleElement=f(),o()(v.A,h),v.A&&v.A.locals&&v.A.locals},825(e){e.exports=function(e){if("undefined"==typeof document)return{update:function(){},remove:function(){}};var n=e.insertStyleElement(e);return{update:function(t){!function(e,n,t){var r="";t.supports&&(r+="@supports (".concat(t.supports,") {")),t.media&&(r+="@media ".concat(t.media," {"));var o=void 0!==t.layer;o&&(r+="@layer".concat(t.layer.length>0?" ".concat(t.layer):""," {")),r+=t.css,o&&(r+="}"),t.media&&(r+="}"),t.supports&&(r+="}");var a=t.sourceMap;a&&"undefined"!=typeof btoa&&(r+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(a))))," */")),n.styleTagTransform(r,e,n.options)}(n,e,t)},remove:function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(n)}}}}}]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var _JUPYTERLAB;(()=>{"use strict";var e,r,t,n,o,a,i,u,l,f,s,d,p,c,h,v,b,g,m,y={246(e,r,t){var n={"./index":()=>t.e(171).then(()=>()=>t(171)),"./extension":()=>t.e(171).then(()=>()=>t(171)),"./style":()=>t.e(728).then(()=>()=>t(728))},o=(e,r)=>(t.R=r,r=t.o(n,e)?n[e]():Promise.resolve().then(()=>{throw new Error('Module "'+e+'" does not exist in container.')}),t.R=void 0,r),a=(e,r)=>{if(t.S){var n="default",o=t.S[n];if(o&&o!==e)throw new Error("Container initialization failed as it has already been initialized with a different share scope");return t.S[n]=e,t.I(n,r)}};t.d(r,{get:()=>o,init:()=>a})}},w={};function j(e){var r=w[e];if(void 0!==r)return r.exports;var t=w[e]={id:e,exports:{}};return y[e](t,t.exports,j),t.exports}j.m=y,j.c=w,j.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return j.d(r,{a:r}),r},j.d=(e,r)=>{for(var t in r)j.o(r,t)&&!j.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},j.f={},j.e=e=>Promise.all(Object.keys(j.f).reduce((r,t)=>(j.f[t](e,r),r),[])),j.u=e=>e+"."+{171:"d366980651e0db8d978c",728:"6552504d5b9b27551bc5"}[e]+".js?v="+{171:"d366980651e0db8d978c",728:"6552504d5b9b27551bc5"}[e],j.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),j.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),e={},r="jl_db_comp:",j.l=(t,n,o,a)=>{if(e[t])e[t].push(n);else{var i,u;if(void 0!==o)for(var l=document.getElementsByTagName("script"),f=0;f<l.length;f++){var s=l[f];if(s.getAttribute("src")==t||s.getAttribute("data-webpack")==r+o){i=s;break}}i||(u=!0,(i=document.createElement("script")).charset="utf-8",j.nc&&i.setAttribute("nonce",j.nc),i.setAttribute("data-webpack",r+o),i.src=t),e[t]=[n];var d=(r,n)=>{i.onerror=i.onload=null,clearTimeout(p);var o=e[t];if(delete e[t],i.parentNode&&i.parentNode.removeChild(i),o&&o.forEach(e=>e(n)),r)return r(n)},p=setTimeout(d.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=d.bind(null,i.onerror),i.onload=d.bind(null,i.onload),u&&document.head.appendChild(i)}},j.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{j.S={};var e={},r={};j.I=(t,n)=>{n||(n=[]);var o=r[t];if(o||(o=r[t]={}),!(n.indexOf(o)>=0)){if(n.push(o),e[t])return e[t];j.o(j.S,t)||(j.S[t]={});var a=j.S[t],i="jl_db_comp",u=[];return"default"===t&&((e,r,t,n)=>{var o=a[e]=a[e]||{},u=o[r];(!u||!u.loaded&&(1!=!u.eager?n:i>u.from))&&(o[r]={get:()=>j.e(171).then(()=>()=>j(171)),from:i,eager:!1})})("jl_db_comp","0.1.1"),e[t]=u.length?Promise.all(u).then(()=>e[t]=1):1}}})(),(()=>{var e;j.g.importScripts&&(e=j.g.location+"");var r=j.g.document;if(!e&&r&&(r.currentScript&&"SCRIPT"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName("script");if(t.length)for(var n=t.length-1;n>-1&&(!e||!/^http(s?):/.test(e));)e=t[n--].src}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),j.p=e})(),t=e=>{var r=e=>e.split(".").map(e=>+e==e?+e:e),t=/^([^-+]+)?(?:-([^+]+))?(?:\+(.+))?$/.exec(e),n=t[1]?r(t[1]):[];return t[2]&&(n.length++,n.push.apply(n,r(t[2]))),t[3]&&(n.push([]),n.push.apply(n,r(t[3]))),n},n=(e,r)=>{e=t(e),r=t(r);for(var n=0;;){if(n>=e.length)return n<r.length&&"u"!=(typeof r[n])[0];var o=e[n],a=(typeof o)[0];if(n>=r.length)return"u"==a;var i=r[n],u=(typeof i)[0];if(a!=u)return"o"==a&&"n"==u||"s"==u||"u"==a;if("o"!=a&&"u"!=a&&o!=i)return o<i;n++}},o=e=>{var r=e[0],t="";if(1===e.length)return"*";if(r+.5){t+=0==r?">=":-1==r?"<":1==r?"^":2==r?"~":r>0?"=":"!=";for(var n=1,a=1;a<e.length;a++)n--,t+="u"==(typeof(u=e[a]))[0]?"-":(n>0?".":"")+(n=2,u);return t}var i=[];for(a=1;a<e.length;a++){var u=e[a];i.push(0===u?"not("+l()+")":1===u?"("+l()+" || "+l()+")":2===u?i.pop()+" "+i.pop():o(u))}return l();function l(){return i.pop().replace(/^\((.+)\)$/,"$1")}},a=(e,r)=>{if(0 in e){r=t(r);var n=e[0],o=n<0;o&&(n=-n-1);for(var i=0,u=1,l=!0;;u++,i++){var f,s,d=u<e.length?(typeof e[u])[0]:"";if(i>=r.length||"o"==(s=(typeof(f=r[i]))[0]))return!l||("u"==d?u>n&&!o:""==d!=o);if("u"==s){if(!l||"u"!=d)return!1}else if(l)if(d==s)if(u<=n){if(f!=e[u])return!1}else{if(o?f>e[u]:f<e[u])return!1;f!=e[u]&&(l=!1)}else if("s"!=d&&"n"!=d){if(o||u<=n)return!1;l=!1,u--}else{if(u<=n||s<d!=o)return!1;l=!1}else"s"!=d&&"n"!=d&&(l=!1,u--)}}var p=[],c=p.pop.bind(p);for(i=1;i<e.length;i++){var h=e[i];p.push(1==h?c()|c():2==h?c()&c():h?a(h,r):!c())}return!!c()},i=(e,r)=>e&&j.o(e,r),u=e=>(e.loaded=1,e.get()),l=e=>Object.keys(e).reduce((r,t)=>(e[t].eager&&(r[t]=e[t]),r),{}),f=(e,r,t)=>{var o=t?l(e[r]):e[r];return Object.keys(o).reduce((e,r)=>!e||!o[e].loaded&&n(e,r)?r:e,0)},s=(e,r,t,n)=>"Unsatisfied version "+t+" from "+(t&&e[r][t].from)+" of shared singleton module "+r+" (required "+o(n)+")",d=e=>{throw new Error(e)},p=e=>{"undefined"!=typeof console&&console.warn&&console.warn(e)},c=(e,r,t)=>t?t():((e,r)=>d("Shared module "+r+" doesn't exist in shared scope "+e))(e,r),h=(e=>function(r,t,n,o,a){var i=j.I(r);return i&&i.then&&!n?i.then(e.bind(e,r,j.S[r],t,!1,o,a)):e(r,j.S[r],t,n,o,a)})((e,r,t,n,o,l)=>{if(!i(r,t))return c(e,t,l);var d=f(r,t,n);return a(o,d)||p(s(r,t,d,o)),u(r[t][d])}),v={},b={206:()=>h("default","@jupyterlab/completer",!1,[1,4,5,2]),428:()=>h("default","@jupyterlab/notebook",!1,[1,4,5,2]),469:()=>h("default","@jupyterlab/coreutils",!1,[1,6,5,2]),490:()=>h("default","@jupyterlab/settingregistry",!1,[1,4,5,2]),830:()=>h("default","@jupyterlab/services",!1,[1,7,5,2])},g={171:[206,428,469,490,830]},m={},j.f.consumes=(e,r)=>{j.o(g,e)&&g[e].forEach(e=>{if(j.o(v,e))return r.push(v[e]);if(!m[e]){var t=r=>{v[e]=0,j.m[e]=t=>{delete j.c[e],t.exports=r()}};m[e]=!0;var n=r=>{delete v[e],j.m[e]=t=>{throw delete j.c[e],r}};try{var o=b[e]();o.then?r.push(v[e]=o.then(t).catch(n)):t(o)}catch(e){n(e)}}})},(()=>{var e={248:0};j.f.j=(r,t)=>{var n=j.o(e,r)?e[r]:void 0;if(0!==n)if(n)t.push(n[2]);else{var o=new Promise((t,o)=>n=e[r]=[t,o]);t.push(n[2]=o);var a=j.p+j.u(r),i=new Error;j.l(a,t=>{if(j.o(e,r)&&(0!==(n=e[r])&&(e[r]=void 0),n)){var o=t&&("load"===t.type?"missing":t.type),a=t&&t.target&&t.target.src;i.message="Loading chunk "+r+" failed.\n("+o+": "+a+")",i.name="ChunkLoadError",i.type=o,i.request=a,n[1](i)}},"chunk-"+r,r)}};var r=(r,t)=>{var n,o,[a,i,u]=t,l=0;if(a.some(r=>0!==e[r])){for(n in i)j.o(i,n)&&(j.m[n]=i[n]);u&&u(j)}for(r&&r(t);l<a.length;l++)o=a[l],j.o(e,o)&&e[o]&&e[o][0](),e[o]=0},t=self.webpackChunkjl_db_comp=self.webpackChunkjl_db_comp||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})(),j.nc=void 0;var S=j(246);(_JUPYTERLAB=void 0===_JUPYTERLAB?{}:_JUPYTERLAB).jl_db_comp=S})();
|
jl_db_comp-0.1.1.data/data/share/jupyter/labextensions/jl_db_comp/static/third-party-licenses.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"packages": [
|
|
3
|
+
{
|
|
4
|
+
"name": "css-loader",
|
|
5
|
+
"versionInfo": "6.11.0",
|
|
6
|
+
"licenseId": "MIT",
|
|
7
|
+
"extractedText": "Copyright JS Foundation and other contributors\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"name": "style-loader",
|
|
11
|
+
"versionInfo": "3.3.4",
|
|
12
|
+
"licenseId": "MIT",
|
|
13
|
+
"extractedText": "Copyright JS Foundation and other contributors\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jl_db_comp
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: A JupyterLab extension to complete db queries in jupyterlab notebooks
|
|
5
5
|
Project-URL: Homepage, https://github.com/Ben-Herz/jl_db_completer
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/Ben-Herz/jl_db_completer/issues
|
|
@@ -300,6 +300,22 @@ the frontend extension, check the frontend extension is installed:
|
|
|
300
300
|
jupyter labextension list
|
|
301
301
|
```
|
|
302
302
|
|
|
303
|
+
### JSONB Autocompletion Not Working
|
|
304
|
+
|
|
305
|
+
If JSONB key completion works on one database but not another, check the server logs (terminal running `jupyter lab`) for diagnostic messages when triggering completion.
|
|
306
|
+
|
|
307
|
+
You can also call the diagnostics endpoint directly:
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
GET /jl-db-comp/jsonb-diagnostics?table=TABLE_NAME&column=COLUMN_NAME
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
This returns the JSONB type distribution and a recommendation. Common causes:
|
|
314
|
+
|
|
315
|
+
- **All NULL values** - The column has no data
|
|
316
|
+
- **Arrays instead of objects** - Keys can only be extracted from `{}` objects, not `[]` arrays
|
|
317
|
+
- **Wrong schema** - Add `&schema=your_schema` if not using `public`
|
|
318
|
+
|
|
303
319
|
## Contributing
|
|
304
320
|
|
|
305
321
|
### Development install
|
|
@@ -359,6 +375,92 @@ In development mode, you will also need to remove the symlink created by `jupyte
|
|
|
359
375
|
command. To find its location, you can run `jupyter labextension list` to figure out where the `labextensions`
|
|
360
376
|
folder is located. Then you can remove the symlink named `jl_db_comp` within that folder.
|
|
361
377
|
|
|
378
|
+
### Contributing Features and Fixes
|
|
379
|
+
|
|
380
|
+
This project uses a **pull request workflow** for all changes. This enables automated changelog generation and streamlined releases.
|
|
381
|
+
|
|
382
|
+
#### Development Workflow
|
|
383
|
+
|
|
384
|
+
**1. Create a feature branch:**
|
|
385
|
+
|
|
386
|
+
```bash
|
|
387
|
+
# For new features
|
|
388
|
+
git checkout -b feature/descriptive-name
|
|
389
|
+
|
|
390
|
+
# For bug fixes
|
|
391
|
+
git checkout -b fix/bug-description
|
|
392
|
+
|
|
393
|
+
# For documentation updates
|
|
394
|
+
git checkout -b docs/what-you-changed
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
**2. Make your changes:**
|
|
398
|
+
|
|
399
|
+
- Write code following the guidelines in `AGENTS.md` / `CLAUDE.md`
|
|
400
|
+
- Add tests for new functionality
|
|
401
|
+
- Update documentation as needed
|
|
402
|
+
- Run tests locally: `pytest -vv -r ap --cov jl_db_comp`
|
|
403
|
+
- Format code: `jlpm run lint`
|
|
404
|
+
|
|
405
|
+
**3. Commit and push:**
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
git add .
|
|
409
|
+
git commit -m "Brief description of changes"
|
|
410
|
+
git push origin feature/descriptive-name
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
**4. Create a Pull Request:**
|
|
414
|
+
|
|
415
|
+
- Go to https://github.com/Ben-Herz/jl_db_completer/pulls
|
|
416
|
+
- Click "New pull request"
|
|
417
|
+
- Select your feature branch
|
|
418
|
+
- **Write a clear PR title** - this becomes the changelog entry!
|
|
419
|
+
- ✅ Good: "Add support for MySQL databases"
|
|
420
|
+
- ✅ Good: "Fix JSONB key completion for nested objects"
|
|
421
|
+
- ❌ Bad: "updates", "fix bug", "changes"
|
|
422
|
+
- Add a description explaining what changed and why
|
|
423
|
+
- **Add a label** (REQUIRED for CI to pass):
|
|
424
|
+
- `enhancement` - New features
|
|
425
|
+
- `bug` - Bug fixes
|
|
426
|
+
- `documentation` - Documentation updates
|
|
427
|
+
- `maintenance` - Maintenance tasks
|
|
428
|
+
|
|
429
|
+
> **Note:** At least one label is required. The "Enforce PR label" CI check will fail if no label is set. You can add labels when creating the PR or afterwards from the PR sidebar.
|
|
430
|
+
|
|
431
|
+
- Click "Create pull request"
|
|
432
|
+
|
|
433
|
+
**5. Review and merge:**
|
|
434
|
+
|
|
435
|
+
- CI checks must pass (build, tests, linting)
|
|
436
|
+
- Address any review feedback
|
|
437
|
+
- Once approved, merge the PR
|
|
438
|
+
- Delete the feature branch after merging
|
|
439
|
+
|
|
440
|
+
#### Automated Releases
|
|
441
|
+
|
|
442
|
+
Releases are created using GitHub Actions workflows:
|
|
443
|
+
|
|
444
|
+
**Step 1: Prepare Release** (Manual workflow)
|
|
445
|
+
|
|
446
|
+
- Go to **Actions** → **"Step 1: Prep Release"** → **"Run workflow"**
|
|
447
|
+
- Optionally specify version (e.g., `0.2.0`, `patch`, `minor`, `major`)
|
|
448
|
+
- The workflow will:
|
|
449
|
+
- Bump the version
|
|
450
|
+
- Generate changelog from merged PRs since last release
|
|
451
|
+
- Create a draft GitHub release
|
|
452
|
+
|
|
453
|
+
**Step 2: Publish Release** (Manual workflow)
|
|
454
|
+
|
|
455
|
+
- Review the draft release
|
|
456
|
+
- Go to **Actions** → **"Step 2: Publish Release"** → **"Run workflow"**
|
|
457
|
+
- The workflow will:
|
|
458
|
+
- Publish to PyPI
|
|
459
|
+
- Publish to npm
|
|
460
|
+
- Make the GitHub release public
|
|
461
|
+
|
|
462
|
+
**Note:** Automated releases require repository secrets for PyPI (`NPM_TOKEN`) and GitHub App credentials. See [RELEASE.md](RELEASE.md) for setup details.
|
|
463
|
+
|
|
362
464
|
### Testing the extension
|
|
363
465
|
|
|
364
466
|
#### Server tests
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
jl_db_comp/__init__.py,sha256=L_Vn61SO_SScZsd1--0ug5RN35CibN9SH-yHBpGXR7g,1102
|
|
2
|
+
jl_db_comp/_version.py,sha256=lFB9nR4WN0iXcp5UpfmTfw1Z2jWBDmClLEWF046NHmc,171
|
|
3
|
+
jl_db_comp/connections.py,sha256=kiNxyQzvd8FlebpCnCk2sFQxlm65L5T2BSZB1H-q7kQ,5222
|
|
4
|
+
jl_db_comp/routes.py,sha256=5u3w5THejkXT9-gz-qKQmJPkDJAVyU2aE0mq_nIn7sE,25798
|
|
5
|
+
jl_db_comp/tests/__init__.py,sha256=mdd3JucOpGsN8PiHaypFnGLdv2nemscFn0piCZc2fls,40
|
|
6
|
+
jl_db_comp/tests/test_routes.py,sha256=gDoRBBtxKPkmXOCvnBeuN1w7SgpqSvrMc5YQrCIFAF4,1752
|
|
7
|
+
jl_db_comp-0.1.1.data/data/etc/jupyter/jupyter_server_config.d/jl_db_comp.json,sha256=fSmY5rlApxKaWIC5WmqvKJ2xW5lLmO_ybLJKogoJcq8,85
|
|
8
|
+
jl_db_comp-0.1.1.data/data/share/jupyter/labextensions/jl_db_comp/package.json,sha256=wtFugy-feEkD99kl3uqBQiE6Aiym3ZeTUHkp7rScNmc,6230
|
|
9
|
+
jl_db_comp-0.1.1.data/data/share/jupyter/labextensions/jl_db_comp/schemas/jl_db_comp/package.json.orig,sha256=ArAswrgu-OKLhksorjeD4bIQz94FRj8oJg3BFl9KutU,7090
|
|
10
|
+
jl_db_comp-0.1.1.data/data/share/jupyter/labextensions/jl_db_comp/schemas/jl_db_comp/plugin.json,sha256=PM45pSfu6jtKNdgUDNumO5-YmQIdCAGO_0x_4Ec6ldg,963
|
|
11
|
+
jl_db_comp-0.1.1.data/data/share/jupyter/labextensions/jl_db_comp/static/171.d366980651e0db8d978c.js,sha256=02aYBlHg242XjFO3IPejU8quOwH1UF3GMbUQ9nSAWcY,8836
|
|
12
|
+
jl_db_comp-0.1.1.data/data/share/jupyter/labextensions/jl_db_comp/static/728.6552504d5b9b27551bc5.js,sha256=ZVJQTVubJ1UbxRj0sICSOtdLfqM4b9Qwf9HLhKXyhb4,4402
|
|
13
|
+
jl_db_comp-0.1.1.data/data/share/jupyter/labextensions/jl_db_comp/static/remoteEntry.48c365d3de0d860be537.js,sha256=SMNl094NhgvlNxRbbZehXIjjOKCF8BhCFk_-YxawEkc,6790
|
|
14
|
+
jl_db_comp-0.1.1.data/data/share/jupyter/labextensions/jl_db_comp/static/style.js,sha256=kHmCPOWdRY7r0Voabh7CMyxQJA6oPseEwKF6mcW--Zs,153
|
|
15
|
+
jl_db_comp-0.1.1.data/data/share/jupyter/labextensions/jl_db_comp/static/third-party-licenses.json,sha256=W6N2sSD7tQihMqQk64F9xMd1Flfr2KO97esAiHUOYdM,2453
|
|
16
|
+
jl_db_comp-0.1.1.data/data/share/jupyter/labextensions/jl_db_comp/install.json,sha256=AkhpkuSkRFqmwkttzIRdh8UApmBhvmiodyEPb9WylXc,181
|
|
17
|
+
jl_db_comp-0.1.1.dist-info/METADATA,sha256=Pw1kd3a_fLqCwmY1aYuzMfSfWGm4uI2V1y1Q9tNCs9M,17251
|
|
18
|
+
jl_db_comp-0.1.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
19
|
+
jl_db_comp-0.1.1.dist-info/licenses/LICENSE,sha256=Oz0oICGKssavDqCu6-PnQedLBaqF1DaZvDyJsGCSIzE,1523
|
|
20
|
+
jl_db_comp-0.1.1.dist-info/RECORD,,
|