UNPKG

spreadsheet-orm

Version:

ORM for Google Spreadsheet - Query Builder and Schema Management for spreadsheet database

3 lines (2 loc) 20.1 kB
var S=class{constructor(e,t,s,r){this.schema=e;this.sheetId=t;this.config=r;this.report=this._initializeReport();let a=s.at(0)??[];this.workingHeaders=[...a],this.evaluableRows=this.removeNonEvaluableRows(s.slice(this.config.sheet.DEFAULT_COLUMN_NAME_SIZE))}report;workingHeaders;evaluableRows;validate(){return this.schema.orderedColumns.forEach((t,s)=>{this._validateField(t,s)}),this._addHeaderCreationRequests(),this.report.fieldsStatus.every(t=>t===null)&&(this.report.stable=!0,this._markReportAsFixable()),this.report}isColumnEntirelyEmpty(e,t){return e.length>0&&t>=e[0].length?!0:e.every(s=>(s[t]??"").trim()==="")}removeNonEvaluableRows(e){let t=[...e].reverse().findIndex(r=>r.some(a=>(a??"").trim()!=="")),s=t===-1?0:e.length-t;return e.slice(0,s)}_initializeReport(){return{stable:!0,fixable:void 0,fieldsStatus:Array(this.schema.orderedColumns.length).fill(!0),unknownHeaders:[],fixRequest:{dataSetting:[],columnMoving:[],headerSetting:[]},schema:this.schema}}_markReportAsFixable(){this.report.fixable===void 0&&(this.report.fixable=!0)}_validateField(e,t){let s=this.workingHeaders.indexOf(e);s<0?this._handleMissingField(t):this._handleExistingField(e,t,s)}_handleMissingField(e){this.report.stable=!1,this.isColumnEntirelyEmpty(this.evaluableRows,e)?this.report.fieldsStatus[e]=null:(this.report.fixable=!1,this.report.fieldsStatus[e]=!1)}_handleExistingField(e,t,s){this._validateFieldData(e,t,s),this._validateFieldPosition(t,s)}_validateFieldData(e,t,s){let r=this.schema.fields[e];if(r.optional===!1&&!this.isColumnEntirelyEmpty(this.evaluableRows,s)&&(this.report.fixable=!1,this.report.stable=!1,this.report.fieldsStatus[t]=!1),r.default!==void 0&&!this.isColumnEntirelyEmpty(this.evaluableRows,s)){this._markReportAsFixable();let a=this._createDefaultValueUpdateRequest(e,s);this.report.fixRequest.dataSetting.push(a)}}_createDefaultValueUpdateRequest(e,t){let s=this.evaluableRows.map(o=>{let u=o[t];return(u??"").trim()===""&&(u=this.schema.fields[e].default?.toString()??""),{values:[{userEnteredValue:{stringValue:u}}]}}),r=this.config.sheet.DATA_STARTING_ROW-1,a=this.config.sheet.columnToNumber(this.config.sheet.DEFAULT_RECORDING_START_COLUMN)-1+t;return{updateCells:{range:{sheetId:this.sheetId,startRowIndex:r,endRowIndex:r+s.length,startColumnIndex:a,endColumnIndex:a+1},rows:s,fields:"userEnteredValue"}}}_validateFieldPosition(e,t){if(t!==e&&this.report.fieldsStatus[e]){this._markReportAsFixable();let s=this._createColumnMoveRequest(e,t);this.report.fixRequest.columnMoving.push(s);let[r]=this.workingHeaders.splice(t,1);this.workingHeaders.splice(e,0,r)}}_createColumnMoveRequest(e,t){let s=this.config.sheet.columnToNumber(this.config.sheet.DEFAULT_RECORDING_START_COLUMN)-1;return{moveDimension:{source:{sheetId:this.sheetId,dimension:"COLUMNS",startIndex:s+t,endIndex:s+t+1},destinationIndex:s+e}}}_addHeaderCreationRequests(){this.report.fieldsStatus.forEach((e,t)=>{if(e===null&&(this.workingHeaders.at(t)??"").trim()===""){this._markReportAsFixable();let s=this._createHeaderUpdateRequest(t);this.report.fixRequest.headerSetting.push(s)}})}_createHeaderUpdateRequest(e){let t=this.config.sheet.columnToNumber(this.config.sheet.DEFAULT_RECORDING_START_COLUMN)-1;return{updateCells:{range:{sheetId:this.sheetId,startRowIndex:this.config.sheet.DEFAULT_RECORDING_START_ROW-1,endRowIndex:this.config.sheet.DEFAULT_RECORDING_START_ROW,startColumnIndex:t+e,endColumnIndex:t+e+1},rows:[{values:[{userEnteredValue:{stringValue:this.schema.orderedColumns[e]}}]}],fields:"userEnteredValue"}}}};var B=class{constructor(e){this.config=e}sheetIDStore;async getSchemaID(e,t=!1){(!this.sheetIDStore||t===!1)&&await this.updateSheetIDStore();let s=this.sheetIDStore?.[e];if(!s)throw Error("sheetIDStore not setted");return s}async sync(e={mode:"strict"}){let t={created:[],skipped:[],fixed:[],written:[],errors:[]},s=await this.getMissingSheets(),r=s.map(c=>c.sheetName);if(r.length)if(this.config.schema.DEFAULT_MISSING_STRATEGY==="create")await this.createSheets(s),t.created=r,r=[];else{if(this.config.schema.DEFAULT_MISSING_STRATEGY==="error")throw t.errors=r,Error("there is no sheet"+s.join(","));this.config.schema.DEFAULT_MISSING_STRATEGY==="ignore"&&(t.skipped=r)}let a=new Set(r),o=this.config.schema.schemaList.filter(c=>{let l=!a.has(c.sheetName);return console.log(l),l});if(await this.updateSheetIDStore(),e.mode==="clean"){let c=await this.makeClearAndWriteRequest(o);return await this.config.spread.API.spreadsheets.batchUpdate({spreadsheetId:this.config.spread.ID,requestBody:{requests:c}}),t.written=o.map(l=>l.sheetName),t}let u=await Promise.all(o.map(c=>this.checkSchemaValidation(c,this.config)));console.log("existingSchemaStableReports",u);let[i,h,p]=u.reduce((c,l)=>{let[f,de,fe]=c;return l.stable?f.push(l):l.fixable?de.push(l):fe.push(l),c},[[],[],[]]);if(console.log("stableReports",i),console.log("fixableReports",h),console.log("unstableReports",p),p.length)if(e.mode==="force"){let c=p.map(f=>f.schema),l=await this.makeClearAndWriteRequest(c);await this.config.spread.API.spreadsheets.batchUpdate({spreadsheetId:this.config.spread.ID,requestBody:{requests:l}}),t.written.concat(c.map(f=>f.sheetName))}else throw t.errors=p.map(c=>c.schema.sheetName),"unstable";if(h.length&&(e.mode==="smart"||e.mode==="force")){let c=h.flatMap(l=>[...l.fixRequest.dataSetting,...l.fixRequest.columnMoving,...l.fixRequest.headerSetting]);c.length&&await this.config.spread.API.spreadsheets.batchUpdate({spreadsheetId:this.config.spread.ID,requestBody:{requests:c}}),t.fixed=h.map(l=>l.schema.sheetName)}if(i.length){let c=i.filter(l=>l.fixable).map(l=>l.schema);if(c.length){let l=await Promise.all(c.map(f=>this.makeWriteSchemaRequest(f)));await this.config.spread.API.spreadsheets.batchUpdate({spreadsheetId:this.config.spread.ID,requestBody:{requests:l}}),t.written.concat(c.map(f=>f.sheetName))}}return t}async checkSchemaValidation(e,t){console.log("checkSchemaValidation",e.sheetName);let s=await this.getSpecifiedSheetData(e);if(s.length===0)return{stable:!0,fieldsStatus:Array(e.orderedColumns.length).fill(null),fixable:!0,schema:e,fixRequest:{dataSetting:[],columnMoving:[],headerSetting:[]}};let r=await this.getSchemaID(e.sheetName,!0);return new S(e,r,s,t).validate()}alter(){}isColumnFullyFilled(e,t){return e.every(s=>(s[t]??"").trim()==="")}removeNonEvaluableRows(e){let t=[...e].reverse().findIndex(r=>r.every(a=>(a??"").trim()==="")),s=t===-1?0:e.length-t;return e.slice(0,s)}async updateSheetIDStore(){let t=(await this.getActualSheets()??[]).map(r=>[r.properties?.title,r.properties?.sheetId]).filter(r=>r[0]!==void 0),s=Object.fromEntries(t);return this.sheetIDStore=s,s}async createSheets(e){let t={spreadsheetId:this.config.spread.ID,resource:{requests:e.map(r=>({addSheet:{properties:{title:r.sheetName}}}))}},s=await this.config.spread.API.spreadsheets.batchUpdate(t);if(s.status!==200)throw Error("error");return s.data.replies}async makeWriteSchemaRequest(e){let t=await this.getSchemaID(e.sheetName,!0),s=this.config.sheet.DEFAULT_RECORDING_START_ROW-1,r=this.config.sheet.columnToNumber(this.config.sheet.DEFAULT_RECORDING_START_COLUMN)-1;return{updateCells:{range:{sheetId:t,startRowIndex:s,endRowIndex:s+1,startColumnIndex:r,endColumnIndex:r+e.orderedColumns.length},rows:[{values:e.orderedColumns.map(a=>({userEnteredValue:{stringValue:a}}))}],fields:"userEnteredValue"}}}async makeClearSheetRequest(e){return{updateCells:{range:{sheetId:await this.getSchemaID(e.sheetName,!0)},fields:"*"}}}async makeClearAndWriteRequest(e){let t=await Promise.all(e.map(r=>this.makeClearSheetRequest(r))),s=await Promise.all(e.map(r=>this.makeWriteSchemaRequest(r)));return[...t,...s]}async getActualSheets(){return(await this.config.spread.getSpreadInfo()).sheets}async getMissingSheets(){let t=(await this.getActualSheets())?.map(a=>a.properties?.title),s=new Set(t);return this.config.schema.schemaList.filter(a=>!s.has(a.sheetName))}async getSpecifiedSheetData(e){let t={startRow:this.config.sheet.DEFAULT_RECORDING_START_ROW+this.config.sheet.DEFAULT_COLUMN_NAME_SIZE-1},s={startColumn:this.config.sheet.DEFAULT_RECORDING_START_COLUMN,endColumn:this.config.sheet.calcColumn(this.config.sheet.DEFAULT_RECORDING_START_COLUMN,Object.keys(e.fields).length)};return(await this.config.spread.API.spreadsheets.values.get({spreadsheetId:this.config.spread.ID,range:this.config.sheet.composeRange(e.sheetName,t,s)})).data.values??[]}},E=B;function y(n){if(n==null)throw new Error("Value is null or undefined")}function ye(n,e){e.forEach(t=>{Object.getOwnPropertyNames(t.prototype).forEach(s=>{Object.defineProperty(n.prototype,s,Object.getOwnPropertyDescriptor(t.prototype,s)||Object.create(null))})})}var C=ye;var q=class{constructor(e){this.config=e}sheetName;specifyColumn(e,t){if(t.length===0)return{};let s=this.config.schema.schemaMap[e];if(!s)return{};let r=t.map(u=>{let i=s.fields[u];return i?i.columnOrder+this.config.sheet.columnToNumber(this.config.sheet.DEFAULT_RECORDING_START_COLUMN):null}).filter(u=>u!==null);if(r.length===0)return{};let a=Math.min(...r),o=Math.max(...r);return{startColumn:this.config.sheet.numberToColumn(a),endColumn:this.config.sheet.numberToColumn(o)}}extractValuesFromMatch(e){return e.map(s=>s.valueRange?.values??[])}},m=q;var M=class extends m{filterFN;where(e){return typeof e=="function"&&(this.filterFN=e),this}async getConditionedData(){let e=this.config.sheet.composeRange(this.sheetName,this.config.sheet.DATA_STARTING_ROW),t=this.makeDataFilters([e]),s=await this.fetchBatchData(this.config.spread.ID,t),r=this.extractValuesFromMatch(s),a=this.indexingBatchData(r[0]);return this.conditioning(a)}getCurrentCondition(){return{filterFN:this.filterFN}}conditioning(e,t={filterFN:this.filterFN}){let{filterFN:s}=t;return s?e.filter(r=>s(r)):e}async fetchBatchData(e,t){let r=(await this.config.spread.API.spreadsheets.values.batchGetByDataFilter({spreadsheetId:e,requestBody:{dataFilters:t}})).data.valueRanges;if(!r)throw Error("fail to fetch data");return r}indexingBatchData(e){return e.map((t,s)=>[s+this.config.sheet.DATA_STARTING_ROW,...t])}makeDataFilters(e){return e.map(t=>({a1Range:t}))}},ee=M;var O=class extends m{},d=O;var k=class extends m{makeNextInstance(...e){let t=this.nextClassConstructor;return new t(this.config,...e)}and(...e){let t=this.makeNextInstance(...e);return this.inheritState(t),console.log("\uB808\uAC70\uC2DCand",t),t}constructor(e){super(e)}},te=k;var x=class extends m{constructor(t,s){super(t);this.nextClassConstructor=s;console.log("constructor\uB294 \uC694\uAE30",this.sheetName)}inheritState(t){t.sheetName=this.sheetName,t.queryQueue=this.queryQueue}saveCurrentQueryToQueue(){this.queryQueue.push(this.createQueryForQueue())}};C(x,[te,d]);var R=x;var b=class extends m{constructor(t,s){super(t);this.nextClassConstructor=s}chainConditioning(t){return t.map((s,r)=>{let a=this.queryQueue[r],o=this.indexingBatchData(s);return this.conditioning(o,a)})}async getChainConditionedData(){this.saveCurrentQueryToQueue();let t=this.queryQueue.map(i=>this.config.sheet.composeRange(i.sheetName,this.config.sheet.DATA_STARTING_ROW)),s=this.makeDataFilters(t),r=await this.fetchBatchData(this.config.spread.ID,s);return this.extractValuesFromMatch(r).map(i=>this.indexingBatchData(i)).map((i,h)=>this.conditioning(i,this.queryQueue[h]))}};C(b,[R,ee]);var T=b;var Q=class extends d{constructor(t,s=[]){super(t);this.targetColumn=s}queryQueue=[];from(t){return new U(this.config,this.targetColumn,t,this.queryQueue)}},se=Q,U=class extends T{constructor(t,s,r,a){super(t,Q);this.targetColumn=s;this.sheetName=r;this.queryQueue=a;this.saveCurrentQueryToQueue()}createQueryForQueue(){return y(this.sheetName),y(this.targetColumn),console.log(this.queryQueue),{...this.getCurrentCondition(),sheetName:this.sheetName,targetColumn:this.targetColumn}}async execute(){let t=this.queryQueue.map(i=>{console.log(i.sheetName);let h=this.specifyColumn(i.sheetName,i.targetColumn);return this.config.sheet.composeRange(i.sheetName,this.config.sheet.DATA_STARTING_ROW,h)});console.log("composedRanges",t);let s=this.makeRequestBody(t);console.log("requestBody",s);let a=(await this.config.spread.API.spreadsheets.values.batchGetByDataFilter({spreadsheetId:this.config.spread.ID,requestBody:s})).data.valueRanges;if(!a)throw Error("error");let o=this.extractValuesFromMatch(a);return this.chainConditioning(o)}makeRequestBody(t){return{dataFilters:t.map(s=>({a1Range:s}))}}};var N=class extends d{constructor(t,s){super(t);this.updateValues=s}queryQueue=[];from(t){return new V(this.config,this.updateValues,t,this.queryQueue)}},re=N,V=class extends T{constructor(t,s,r,a){super(t,N);this.updateValues=s;this.sheetName=r;this.queryQueue=a;this.saveCurrentQueryToQueue()}async execute(){let s=(await this.getChainConditionedData()).map((a,o)=>{let{updateValues:u,sheetName:i}=this.queryQueue[o],h=a.flatMap(p=>{let c=p.at(0);return this.config.sheet.composeRange(i,{startRow:c,endRow:c})});return this.makeUpdateDataArr(h,u)}).flat(),r=await this.config.spread.API.spreadsheets.values.batchUpdateByDataFilter({spreadsheetId:this.config.spread.ID,requestBody:{data:s,valueInputOption:"RAW"}});if(r.status!==200)throw Error("error");return r.data.totalUpdatedRows}createQueryForQueue(){return{...this.getCurrentCondition(),sheetName:this.sheetName,updateValues:this.updateValues}}makeUpdateDataArr(t,s){return Array.isArray(s)?t.reduce((r,a)=>(r.push({dataFilter:{a1Range:a},majorDimension:"ROWS",values:[s]}),r),[]):[]}};var D=class extends d{queryQueue=[];from(e){return new W(this.config,e,this.queryQueue)}constructor(e){super(e)}},ae=D,W=class extends T{constructor(t,s,r){super(t,D);this.sheetName=s;this.queryQueue=r;this.saveCurrentQueryToQueue()}createQueryForQueue(){return y(this.sheetName),{sheetName:this.sheetName,filterFN:this.filterFN}}async execute(){let s=(await this.getChainConditionedData()).map((a,o)=>{let{sheetName:u}=this.queryQueue[o],i=a.flatMap(h=>{let p=h.at(0);return this.config.sheet.composeRange(u,{startRow:p,endRow:p})});return this.makeDeleteDataArr(i)}).flat(),r=await this.config.spread.API.spreadsheets.values.batchClearByDataFilter({spreadsheetId:this.config.spread.ID,requestBody:{dataFilters:s}});if(r.status!==200)throw Error("error");return r.data.clearedRanges}makeDeleteDataArr(t){return t.reduce((s,r)=>(s.push({a1Range:r}),s),[])}};var I=class extends d{constructor(t,s){super(t);this.insertValues=s}queryQueue=[];into(t){return console.log("insert builder > settedInsertBuilder \uC0DD\uC131",this.queryQueue),new P(this.config,this.insertValues,t,this.queryQueue)}},ne=I,P=class extends R{constructor(t,s,r,a){super(t,I);this.insertValues=s;this.sheetName=r;this.queryQueue=a;this.saveCurrentQueryToQueue()}createQueryForQueue(){return{sheetName:this.sheetName,insertValues:this.insertValues}}async execute(){let t=new Map;for(let a of this.queryQueue)t.has(a.sheetName)||t.set(a.sheetName,[]),t.get(a.sheetName).push(a.insertValues);let s=[...t.entries()].map(([a,o])=>this.config.spread.API.spreadsheets.values.append({spreadsheetId:this.config.spread.ID,valueInputOption:"RAW",range:a,requestBody:{values:o}}));return(await Promise.all(s)).map(a=>{if(a.status!==200)throw Error("error");return a.data.updates?.updatedRows})}};var L=class{constructor(e){this.config=e}insert(e){return console.log("qeuryBuildr > insert"),new ne(this.config,e)}select(e){return new se(this.config,e)}update(e){return new re(this.config,e)}delete(){return new ae(this.config)}},G=L;var $=class{missingSchemaStartegy;DEFAULT_MISSING_STRATEGY="create";schemaList;schemaMap;makeSchemaMap(e){return e.reduce((s,r)=>{let a=r.sheetName;return s[a]=r,s},{})}constructor(e){this.missingSchemaStartegy=e.onMissingSchema??this.DEFAULT_MISSING_STRATEGY,this.schemaList=e.schemas??[],this.schemaMap=this.makeSchemaMap(this.schemaList)}},ie=$;import{GaxiosError as Te}from"gaxios";import{google as oe}from"googleapis";var K=class n{static makeAuthJWT({email:e,privateKey:t}){return new oe.auth.JWT({email:e,key:t,scopes:["https://www.googleapis.com/auth/spreadsheets"]})}static extractSheetIDfromURL(e){let t=/\/d\/([a-zA-Z0-9_-]{43})/,s=e.match(t);return s&&s[1]?s[1]:!1}ID;authJWT;API;info=null;constructor(e){this.checkFormat(e),this.ID=n.extractSheetIDfromURL(e.spreadsheetID)||e.spreadsheetID,this.authJWT=n.makeAuthJWT({email:e.email,privateKey:e.privateKey}),this.API=oe.sheets({version:"v4",auth:this.authJWT})}async getSpreadInfo({cached:e}={cached:!1}){if(e&&this.info)return this.info;let t=this.ID;try{this.API.spreadsheets.values;let s=await this.API.spreadsheets.get({spreadsheetId:t});return this.info=s.data,this.info}catch(s){if(s instanceof Te){let r=s.status,a=s.response?.data.error.message;throw r===404?new Error(`cannot find spreadsheet with (ID:${t})`):r===403?new Error(`forbidden spreadsheet with (ID:${t})`):new Error(`Error fetching spreadsheet: ${r} - ${a}`)}throw new Error(`Unexpected error: ${s.message}`)}}checkFormat(e){if(!this.isValidEmail(e.email))throw Error("Invalid email format")}isValidEmail(e){return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e)}},ue=K;var H=class{DEFAULT_RECORDING_START_ROW=1;DEFAULT_RECORDING_START_COLUMN="A";DEFAULT_COLUMN_NAME_SIZE=1;DATA_STARTING_ROW=this.DEFAULT_RECORDING_START_ROW+this.DEFAULT_COLUMN_NAME_SIZE;DATA_STARTING_CELL={column:this.DEFAULT_RECORDING_START_COLUMN,row:this.DATA_STARTING_ROW};parseCell(e){let t=e.match(/^([A-Z]+)(\d+)$/);if(!t)throw new Error("Invalid A1 cell address format");let s=t[1],r=parseInt(t[2],10);return{column:s,row:r}}parseRange(e){let[t,s]=e.split("!"),r=s!==void 0,a=r?t:void 0,o=r?s:t,[u,i]=o.split(":");if(!u)throw new Error("Invalid range format: startCell is missing");let h=this.parseCell(u),p=i?this.parseCell(i):void 0;return{sheetName:a,startCell:h,endCell:p}}composeRange(e,t,s){let r={startColumn:"A",endColumn:"ZZZ",...s},a=typeof t=="number",o=a?t:t.startRow,u=a?"":t.endRow??t.startRow,{startColumn:i,endColumn:h}=r;return`${e}!${i}${o}:${h}${u}`}numberToColumn(e){if(e<1)throw new Error("Column number must be >= 1");if(e>18278)throw new Error("Google Sheets supports up to 18278 columns (ZZZ)");let t="";for(;e>0;)e--,t=String.fromCharCode(e%26+65)+t,e=Math.floor(e/26);return t}columnToNumber(e){let t=0,s=e.toUpperCase();for(let r=0;r<s.length;r++){let a=s.charCodeAt(r)-64;if(a<1||a>26)throw new Error("Invalid column letter: "+e);t=t*26+a}return t}calcColumn(e,t){let r=this.columnToNumber(e)+t;return this.numberToColumn(r)}constructor(e){}},ce=H;var Z=class{spread;sheet;schema;constructor(e){this.spread=new ue(e),this.sheet=new ce(e),this.schema=new ie(e)}},j=Z;var J=class{constructor(e,t,s){this.configs=e;this.queryBuilder=t;this.schemaManager=s;this.spreadsheetAPI=this.configs.spread.API,this.spreadsheetID=this.configs.spread.ID}spreadsheetAPI;spreadsheetID;query(e,t){return e===void 0?this.queryBuilder:this.executeSql(e,t)}async executeSql(e,t){}},Y=J;function ge(n){let e=new j(n);return new Y(e,new G(e),new E(e))}var le=ge;var z=class{_optional=!1;_default=void 0;optional(){return this._optional=!0,this}default(e){return this._default=e,this}build(){return{dataType:this.getType(),optional:this._optional,default:this._default}}},g=z;var w=class extends g{getType(){return"string"}},v=class extends g{getType(){return"number"}},A=class extends g{getType(){return"boolean"}},_=class extends g{getType(){return"date"}},F=class extends g{constructor(t,s){super();this.refSchema=t;this.field=s}getType(){return this.refSchema.fields[this.field].dataType}};var X=class{constructor(e,t,s){this.sheetName=e;this.fields=t;this.orderedColumns=s}},he=X;function pe(n,e,t){let s;typeof e=="function"?s=e(me):s=e;let r=new Set(t),a=Object.keys(s);for(let i of a)r.add(i);let o=[...r],u={};return o.forEach((i,h)=>{let p=s[i];u[i]={...p,columnOrder:h+1}}),console.log(n,"orderedKeys",o),new he(n,u,o)}var me={boolean:()=>new A,date:()=>new _,number:()=>new v,string:()=>new w,reference:(n,e)=>new F(n,e)};var Lt=le;export{j as Configs,G as QueryBuilder,E as SchemaManager,Y as SpreadsheetClient,le as createSpreadsheetClient,Lt as default,pe as defineTable,me as fieldBuilder}; //# sourceMappingURL=index.js.map