@jybk/pptb-data-dictionary-builder 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +63 -2
- package/dist/app.js +8 -0
- package/dist/icons/data-dictionary-builder.svg +5 -0
- package/dist/index.html +80 -0
- package/dist/styles.css +537 -0
- package/npm-shrinkwrap.json +637 -0
- package/package.json +61 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jay
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,64 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Data Dictionary Builder
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A tool for [Power Platform ToolBox](https://www.powerplatformtoolbox.com). Paste the address of a model-driven app form or view and get a data dictionary of the fields on it: in the order they appear on screen, grouped by tab and section, with the usual data dictionary columns.
|
|
4
|
+
|
|
5
|
+
Other metadata exporters list every column of a table. This one lists what a user actually sees on one screen, which is what you need for training material, UAT scripts, migration mapping workshops and functional design documents.
|
|
6
|
+
|
|
7
|
+
## Use
|
|
8
|
+
|
|
9
|
+
1. Open the tool in ToolBox and select a connection.
|
|
10
|
+
2. Paste an address and press **Build dictionary**. Accepted:
|
|
11
|
+
- a record address from a model-driven app (`…/main.aspx?appid=…&pagetype=entityrecord&etn=account&id=…&formid=…`)
|
|
12
|
+
- a view address (`…&pagetype=entitylist&etn=account&viewid=…`)
|
|
13
|
+
- a form or view designer address from make.powerapps.com
|
|
14
|
+
- a table logical name (`account`) or a form/view id
|
|
15
|
+
3. If the address does not say which form (record addresses often leave `formid` out), pick one from the list. Forms that belong to the app in the address are marked.
|
|
16
|
+
4. Filter by tab, choose columns, then export to Excel, CSV, Markdown or JSON, or copy the table.
|
|
17
|
+
|
|
18
|
+
## Output
|
|
19
|
+
|
|
20
|
+
Rows follow the order of appearance: header, each tab left to right (sections and fields top to bottom), footer. For views, columns left to right.
|
|
21
|
+
|
|
22
|
+
| Column | Default | Notes |
|
|
23
|
+
| --- | --- | --- |
|
|
24
|
+
| #, Tab, Section, Label | on | Label is the label on the form; falls back to the column display name |
|
|
25
|
+
| Logical Name, Data Type, Format, Length / Range | on | |
|
|
26
|
+
| Required, Read-only | on | Read-only = locked on the form, or the column can never be edited |
|
|
27
|
+
| Allowed Values, Default, Lookup Targets | on | Choice values as `value = label` |
|
|
28
|
+
| Description, Custom | on | |
|
|
29
|
+
| Display Name, Schema Name, Table, Visible, Source, Searchable, Audited, Column Security, Control, Notes | off | Turn on under **Columns** |
|
|
30
|
+
|
|
31
|
+
Options: include hidden tabs/sections/fields, include subgrids/quick views/web resources, list each field once, one Excel worksheet per tab, label language (when the form has labels in more than one language).
|
|
32
|
+
|
|
33
|
+
The Excel workbook also has an **Allowed Values** sheet (one row per choice) and an **About** sheet (source address, environment, date, scope).
|
|
34
|
+
|
|
35
|
+
## Limits
|
|
36
|
+
|
|
37
|
+
- Reflects the saved form or view design. Business rules, scripts and security roles can show, hide or lock fields at runtime, and the tool cannot see that.
|
|
38
|
+
- Model-driven forms and views only. Canvas apps, custom pages, Power Pages and dashboards are not supported.
|
|
39
|
+
- Business process flow stages and fields inside quick view forms are not expanded (a quick view appears as one control when "include subgrids…" is on).
|
|
40
|
+
- Read-only: it reads form, view and column definitions, never records, and writes nothing to Dataverse.
|
|
41
|
+
|
|
42
|
+
## Develop
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install
|
|
46
|
+
npm run build # type-check and bundle into dist/
|
|
47
|
+
npm test # unit tests for URL parsing, form/view parsing, dictionary rows and exports
|
|
48
|
+
npm run preview # builds preview/ with mock data; open preview/index.html?demo=form in a browser
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Load into ToolBox: Settings → Show Debug Menu, then Debug → Load Local Tool → select this folder.
|
|
52
|
+
|
|
53
|
+
## Publish
|
|
54
|
+
|
|
55
|
+
1. Fill in the owner details (GitHub user or organisation, author name, optional npm scope):
|
|
56
|
+
`node scripts/set-identity.mjs <github-user> "<Author Name>" [npm-scope]` (already set to `jybk` / `@jybk`)
|
|
57
|
+
2. Create the public GitHub repository `pptb-data-dictionary-builder` and push `main`. The ToolBox validator checks that the repository and README URLs resolve, so push before validating.
|
|
58
|
+
3. `npm install && npm run build && npm test && npm run validate`
|
|
59
|
+
4. `npm run finalize-package` then `npm publish --access public`
|
|
60
|
+
5. Submit the npm package name at https://www.powerplatformtoolbox.com/submit-tool
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
MIT
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";(()=>{var le=[{key:"order",header:"#",on:!0,width:5},{key:"tab",header:"Tab",on:!0,width:18},{key:"section",header:"Section",on:!0,width:20},{key:"label",header:"Label",on:!0,width:26},{key:"displayName",header:"Display Name",on:!1,width:26},{key:"logicalName",header:"Logical Name",on:!0,width:26,mono:!0},{key:"schemaName",header:"Schema Name",on:!1,width:26,mono:!0},{key:"table",header:"Table",on:!1,width:18,mono:!0},{key:"dataType",header:"Data Type",on:!0,width:18},{key:"format",header:"Format",on:!0,width:16},{key:"size",header:"Length / Range",on:!0,width:24},{key:"required",header:"Required",on:!0,width:14},{key:"readOnly",header:"Read-only",on:!0,width:10},{key:"visible",header:"Visible",on:!1,width:9},{key:"choices",header:"Allowed Values",on:!0,width:34},{key:"defaultValue",header:"Default",on:!0,width:16},{key:"targets",header:"Lookup Targets",on:!0,width:22,mono:!0},{key:"description",header:"Description",on:!0,width:44},{key:"custom",header:"Custom",on:!0,width:8},{key:"source",header:"Source",on:!1,width:12},{key:"searchable",header:"Searchable",on:!1,width:11},{key:"audited",header:"Audited",on:!1,width:9},{key:"secured",header:"Column Security",on:!1,width:15},{key:"controlType",header:"Control",on:!1,width:14},{key:"notes",header:"Notes",on:!1,width:34}];function Fe(e,t){if(e[t])return e[t];let n=Object.keys(e)[0];return n?e[Number(n)]:""}function oe(e,t){if(!e)return"";let n=e.LocalizedLabels??[];return(n.find(i=>i.LanguageCode===t)?.Label??e.UserLocalizedLabel?.Label??n[0]?.Label??"").trim()}var jt={StringType:"Text",MemoType:"Multiline Text",IntegerType:"Whole Number",BigIntType:"Big Integer",DecimalType:"Decimal Number",DoubleType:"Floating Point Number",MoneyType:"Currency",BooleanType:"Yes/No",DateTimeType:"Date and Time",PicklistType:"Choice",MultiSelectPicklistType:"Choices (multi-select)",StateType:"Status",StatusType:"Status Reason",LookupType:"Lookup",CustomerType:"Customer",OwnerType:"Owner",UniqueidentifierType:"Unique Identifier",ImageType:"Image",FileType:"File",PartyListType:"Activity Party",EntityNameType:"Table Name",VirtualType:"Virtual",ManagedPropertyType:"Managed Property",CalendarRulesType:"Calendar Rules"},Ht={None:"Optional",Recommended:"Recommended",ApplicationRequired:"Required",SystemRequired:"System Required"},Kt={0:"Simple",1:"Calculated",2:"Rollup",3:"Formula"};function pe(e){return e.AttributeTypeName?.Value??(e.AttributeType?`${e.AttributeType}Type`:"")}function Jt(e){let t=pe(e);return t==="DateTimeType"&&(e.Format==="DateOnly"||e.DateTimeBehavior?.Value==="DateOnly")?"Date Only":jt[t]??t.replace(/Type$/,"")}function Gt(e){let t=pe(e);if(t==="StringType"||t==="MemoType"){let n=e.FormatName?.Value??e.Format??"";return n==="Text"||n==="TextArea"?"":n}return t==="IntegerType"?e.Format&&e.Format!=="None"?e.Format:"":t==="DateTimeType"?e.DateTimeBehavior?.Value??"":""}function ie(e){return typeof e=="number"?e.toLocaleString("en-US",{maximumFractionDigits:10}):""}function Xt(e){let t=pe(e);if(t==="StringType"||t==="MemoType")return e.MaxLength!=null?`Max ${ie(e.MaxLength)} characters`:"";if(t==="IntegerType"||t==="BigIntType")return e.MinValue!=null&&e.MaxValue!=null?`${ie(e.MinValue)} to ${ie(e.MaxValue)}`:"";if(t==="DecimalType"||t==="DoubleType"||t==="MoneyType"){let n=e.MinValue!=null&&e.MaxValue!=null?`${ie(e.MinValue)} to ${ie(e.MaxValue)}`:"",r=e.Precision!=null?`${e.Precision} decimals`:"";return[n,r].filter(Boolean).join("; ")}return(t==="FileType"||t==="ImageType")&&e.MaxSizeInKB!=null?`Max ${ie(e.MaxSizeInKB)} KB`:""}function se(e){return e===!0?"Yes":e===!1?"No":""}function Zt(e,t){return e?e.TrueOption||e.FalseOption?[e.TrueOption,e.FalseOption].filter(Boolean).map(n=>({value:n.Value,label:oe(n.Label,t)})):(e.Options??[]).map(n=>({value:n.Value,label:oe(n.Label,t)})):[]}function Wt(e,t){let n=pe(e),r;return n==="BooleanType"?r=e.DefaultValue===!0?1:e.DefaultValue===!1?0:void 0:(n==="PicklistType"||n==="StateType"||n==="StatusType")&&(r=e.DefaultFormValue),typeof r!="number"||r===-1?"":t.find(i=>i.value===r)?.label??String(r)}function Qe(e,t,n,r){let{lcid:i}=r,o=[],l=[],a=new Map,c=new Set,f=new Set,u=new Set,p=0,m=0,w=0,b=0;for(let h of e){let x=h.controlType==="Field"&&!!h.fieldName,I=!(h.tabVisible&&h.sectionVisible&&h.cellVisible);if(!x&&!r.includeControls){m++;continue}if(I&&!r.includeHidden){p++;continue}let T=h.table||t,z=`${T}.${h.fieldName}`;if(x&&r.uniqueOnly){if(c.has(z)){w++;continue}c.add(z)}let $=h.area==="Body"?Fe(h.tabLabels,i)||h.tabName:h.area==="View"?"View columns":h.area,C=n.get(T),d=x?C?.attributes.get(h.fieldName):void 0,S=x?C?.optionSets.get(h.fieldName):void 0,v=Zt(S,i),g=d?oe(d.DisplayName,i):"",N=Fe(h.cellLabels,i)||g||h.fieldName||h.controlId,O=[];h.detail&&O.push(h.detail),x&&!d&&(b++,O.push(T.startsWith("?")?`Column of a linked table (alias "${T.slice(1)}") that could not be resolved`:"No column metadata found for this field")),I&&O.push("Hidden by default"),u.add(T);let P={order:o.length+1,tab:$,section:Fe(h.sectionLabels,i)||h.sectionName,label:N,displayName:g,logicalName:h.fieldName??"",schemaName:d?.SchemaName??"",table:x?T.replace(/^\?/,""):"",dataType:d?Jt(d):x?"":h.controlType,format:d?Gt(d):"",size:d?Xt(d):"",required:d?Ht[d.RequiredLevel?.Value]??"":"",readOnly:!x||h.area==="View"?"":se(h.disabled||!!d&&d.IsValidForUpdate===!1&&d.IsValidForCreate===!1),visible:I?"No":"Yes",choices:v.map(M=>`${M.value} = ${M.label}`).join(`
|
|
2
|
+
`),defaultValue:d?Wt(d,v):"",targets:d&&Array.isArray(d.Targets)?d.Targets.join(", "):"",description:d?oe(d.Description,i):"",custom:d?se(d.IsCustomAttribute):"",source:d&&typeof d.SourceType=="number"?Kt[d.SourceType]??"":"",searchable:d?se(d.IsValidForAdvancedFind?.Value):"",audited:d?se(d.IsAuditEnabled?.Value):"",secured:d?se(d.IsSecured):"",controlType:h.controlType,notes:O.join("; ")};o.push({groupKey:h.groupKey,values:P,hidden:I,isField:x});let K=a.get(h.groupKey)??{key:h.groupKey,label:$,count:0};if(K.count++,a.set(h.groupKey,K),v.length&&!f.has(z)){f.add(z);for(let M of v)l.push({table:T,logicalName:h.fieldName,label:N,value:M.value,choice:M.label})}}return{rows:o,choices:l,tabs:[...a.values()],hiddenSkipped:p,controlsSkipped:m,repeatsSkipped:w,missingMetadata:b,multipleTables:u.size>1}}var me="[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}",Yt=new RegExp(`^\\{?(${me})\\}?$`,"i"),Qt=/^[a-z][a-z0-9_]*$/;function de(e){if(!e)return;let t=e.trim();try{t=decodeURIComponent(t)}catch{}let n=Yt.exec(t.trim());return n?n[1].toLowerCase():void 0}function ce(e){return!!e&&Qt.test(e)}function De(e,t){new URLSearchParams(e.replace(/^[?#]/,"")).forEach((r,i)=>{let o=i.toLowerCase();t.has(o)||t.set(o,r)})}function _e(e){let t=(e??"").trim(),n={kind:"unknown",raw:t};if(!t)return n;let r=de(t);if(r)return{...n,kind:"id",anyId:r};if(ce(t.toLowerCase())&&!t.includes("."))return{...n,kind:"table",etn:t.toLowerCase(),prefer:"form"};let i;try{i=new URL(/^[a-z][a-z0-9+.-]*:\/\//i.test(t)?t:`https://${t}`)}catch{return n}if(!i.hostname.includes("."))return n;n.host=i.hostname.toLowerCase(),n.isMakerPortal=/(^|\.)make\.(preview\.)?powerapps\.|(^|\.)make\.powerautomate\./.test(n.host)||/powerapps\.(com|us|cn)$/.test(n.host);let o=new Map;De(i.search,o),i.hash.includes("=")&&De(i.hash,o);let l=o.get("extraqs");l&&De(l,o);let a=o.get("etn")?.toLowerCase();ce(a)&&(n.etn=a),n.formId=de(o.get("formid")),n.viewId=de(o.get("viewid")),n.appId=de(o.get("appid"));let c=(o.get("pagetype")??"").toLowerCase(),f=i.pathname,u=new RegExp(`/forms?/(?:edit/)?(${me})`,"i").exec(f),p=new RegExp(`/views?/(?:edit/)?(${me})`,"i").exec(f),m=new RegExp(`/entit(?:y|ies)/(?:${me}/)?([a-z][a-z0-9_]*)`,"i").exec(f);return!n.formId&&u&&(n.formId=u[1].toLowerCase()),!n.viewId&&p&&(n.viewId=p[1].toLowerCase()),!n.etn&&m&&ce(m[1].toLowerCase())&&(n.etn=m[1].toLowerCase()),c==="entitylist"&&n.viewId?n.kind="view":n.formId?n.kind="form":n.viewId?n.kind="view":n.etn&&(n.kind="table",n.prefer=c==="entitylist"||/\/views?\/?$/i.test(f)?"view":"form"),n}function et(e,t){if(!e.host||e.isMakerPortal||!t)return null;let n;try{n=new URL(t).hostname.toLowerCase()}catch{return null}return n===e.host?null:`The URL points to ${e.host}, but the active connection is ${n}. The dictionary is built from the active connection.`}var tt={2:"Main form",6:"Quick view form",7:"Quick create form",11:"Card form"};async function J(e){let t=await window.dataverseAPI.queryData(e);return Array.isArray(t?.value)?t.value:[]}async function _t(e){return await window.dataverseAPI.queryData(e)}function nt(e){if(!ce(e))throw new Error(`"${e}" is not a valid table logical name.`);return e}async function ge(e){let n=(await J(`systemforms?$select=formid,name,type,objecttypecode,formxml,description&$filter=formid eq ${e}`))[0];return n?{kind:"form",id:n.formid,name:n.name??"(unnamed form)",typeLabel:tt[n.type]??"Form",etn:String(n.objecttypecode??"").toLowerCase(),description:n.description??"",xml:n.formxml??"",fetchXml:""}:null}async function ve(e){let n=(await J(`savedqueries?$select=savedqueryid,name,returnedtypecode,layoutxml,fetchxml,description&$filter=savedqueryid eq ${e}`))[0],r="System view";return n||(n=(await J(`userqueries?$select=userqueryid,name,returnedtypecode,layoutxml,fetchxml,description&$filter=userqueryid eq ${e}`).catch(()=>[]))[0],r="Personal view"),n?{kind:"view",id:n.savedqueryid??n.userqueryid,name:n.name??"(unnamed view)",typeLabel:r,etn:String(n.returnedtypecode??"").toLowerCase(),description:n.description??"",xml:n.layoutxml??"",fetchXml:n.fetchxml??""}:null}async function en(e){try{let n=(await J(`appmodules?$select=appmoduleidunique&$filter=appmoduleid eq ${e}`))[0]?.appmoduleidunique;if(!n)return new Set;let r=await J(`appmodulecomponents?$select=objectid&$filter=_appmoduleidunique_value eq ${n} and componenttype eq 60`);return new Set(r.map(i=>String(i.objectid).toLowerCase()))}catch{return new Set}}async function rt(e,t){let n=nt(e),[r,i,o]=await Promise.all([J(`systemforms?$select=formid,name,type,description&$filter=objecttypecode eq '${n}' and (type eq 2 or type eq 6 or type eq 7) and formactivationstate eq 1&$orderby=type asc,name asc`),J(`savedqueries?$select=savedqueryid,name,description,isdefault&$filter=returnedtypecode eq '${n}' and querytype eq 0 and statecode eq 0&$orderby=name asc`).catch(()=>[]),t?en(t):Promise.resolve(new Set)]),l=[];for(let a of r)l.push({kind:"form",id:a.formid,name:a.name??"(unnamed form)",typeLabel:tt[a.type]??"Form",description:a.description??"",inApp:o.has(String(a.formid).toLowerCase()),isDefault:!1});for(let a of i)l.push({kind:"view",id:a.savedqueryid,name:a.name??"(unnamed view)",typeLabel:"System view",description:a.description??"",inApp:!1,isDefault:a.isdefault===!0});return l}var tn=["Picklist","MultiSelectPicklist","State","Status","Boolean"];async function it(e,t){let n=nt(e),r=`EntityDefinitions(LogicalName='${n}')`,[i,o,...l]=await Promise.all([_t(`${r}?$select=LogicalName,DisplayName`),J(`${r}/Attributes`),...tn.map(c=>J(`${r}/Attributes/Microsoft.Dynamics.CRM.${c}AttributeMetadata?$select=LogicalName&$expand=OptionSet`).catch(()=>[]))]),a={logicalName:n,displayName:oe(i?.DisplayName,t)||n,attributes:new Map,optionSets:new Map};for(let c of o)c.LogicalName&&a.attributes.set(c.LogicalName,c);for(let c of l)for(let f of c)f.LogicalName&&f.OptionSet&&a.optionSets.set(f.LogicalName,f.OptionSet);return a}var A=Uint8Array,R=Uint16Array,Re=Int32Array,Oe=new A([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),qe=new A([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),ot=new A([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),ft=function(e,t){for(var n=new R(31),r=0;r<31;++r)n[r]=t+=1<<e[r-1];for(var i=new Re(n[30]),r=1;r<30;++r)for(var o=n[r];o<n[r+1];++o)i[o]=o-n[r]<<5|r;return{b:n,r:i}},ht=ft(Oe,2),nn=ht.b,Ee=ht.r;nn[28]=258,Ee[258]=28;var pt=ft(qe,0),Vn=pt.b,at=pt.r,Ae=new R(32768);for(y=0;y<32768;++y)G=(y&43690)>>1|(y&21845)<<1,G=(G&52428)>>2|(G&13107)<<2,G=(G&61680)>>4|(G&3855)<<4,Ae[y]=((G&65280)>>8|(G&255)<<8)>>1;var G,y,he=(function(e,t,n){for(var r=e.length,i=0,o=new R(t);i<r;++i)e[i]&&++o[e[i]-1];var l=new R(t);for(i=1;i<t;++i)l[i]=l[i-1]+o[i-1]<<1;var a;if(n){a=new R(1<<t);var c=15-t;for(i=0;i<r;++i)if(e[i])for(var f=i<<4|e[i],u=t-e[i],p=l[e[i]-1]++<<u,m=p|(1<<u)-1;p<=m;++p)a[Ae[p]>>c]=f}else for(a=new R(r),i=0;i<r;++i)e[i]&&(a[i]=Ae[l[e[i]-1]++]>>15-e[i]);return a}),_=new A(288);for(y=0;y<144;++y)_[y]=8;var y;for(y=144;y<256;++y)_[y]=9;var y;for(y=256;y<280;++y)_[y]=7;var y;for(y=280;y<288;++y)_[y]=8;var y,ye=new A(32);for(y=0;y<32;++y)ye[y]=5;var y,rn=he(_,9,0);var on=he(ye,5,0);var dt=function(e){return(e+7)/8|0},mt=function(e,t,n){return(t==null||t<0)&&(t=0),(n==null||n>e.length)&&(n=e.length),new A(e.subarray(t,n))};var an=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"],we=function(e,t,n){var r=new Error(t||an[e]);if(r.code=e,Error.captureStackTrace&&Error.captureStackTrace(r,we),!n)throw r;return r};var X=function(e,t,n){n<<=t&7;var r=t/8|0;e[r]|=n,e[r+1]|=n>>8},ue=function(e,t,n){n<<=t&7;var r=t/8|0;e[r]|=n,e[r+1]|=n>>8,e[r+2]|=n>>16},Ne=function(e,t){for(var n=[],r=0;r<e.length;++r)e[r]&&n.push({s:r,f:e[r]});var i=n.length,o=n.slice();if(!i)return{t:vt,l:0};if(i==1){var l=new A(n[0].s+1);return l[n[0].s]=1,{t:l,l:1}}n.sort(function(C,d){return C.f-d.f}),n.push({s:-1,f:25001});var a=n[0],c=n[1],f=0,u=1,p=2;for(n[0]={s:-1,f:a.f+c.f,l:a,r:c};u!=i-1;)a=n[n[f].f<n[p].f?f++:p++],c=n[f!=u&&n[f].f<n[p].f?f++:p++],n[u++]={s:-1,f:a.f+c.f,l:a,r:c};for(var m=o[0].s,r=1;r<i;++r)o[r].s>m&&(m=o[r].s);var w=new R(m+1),b=ze(n[u-1],w,0);if(b>t){var r=0,h=0,x=b-t,I=1<<x;for(o.sort(function(d,S){return w[S.s]-w[d.s]||d.f-S.f});r<i;++r){var T=o[r].s;if(w[T]>t)h+=I-(1<<b-w[T]),w[T]=t;else break}for(h>>=x;h>0;){var z=o[r].s;w[z]<t?h-=1<<t-w[z]++-1:++r}for(;r>=0&&h;--r){var $=o[r].s;w[$]==t&&(--w[$],++h)}b=t}return{t:new A(w),l:b}},ze=function(e,t,n){return e.s==-1?Math.max(ze(e.l,t,n+1),ze(e.r,t,n+1)):t[e.s]=n},st=function(e){for(var t=e.length;t&&!e[--t];);for(var n=new R(++t),r=0,i=e[0],o=1,l=function(c){n[r++]=c},a=1;a<=t;++a)if(e[a]==i&&a!=t)++o;else{if(!i&&o>2){for(;o>138;o-=138)l(32754);o>2&&(l(o>10?o-11<<5|28690:o-3<<5|12305),o=0)}else if(o>3){for(l(i),--o;o>6;o-=6)l(8304);o>2&&(l(o-3<<5|8208),o=0)}for(;o--;)l(i);o=1,i=e[a]}return{c:n.subarray(0,r),n:t}},fe=function(e,t){for(var n=0,r=0;r<t.length;++r)n+=e[r]*t[r];return n},gt=function(e,t,n){var r=n.length,i=dt(t+2);e[i]=r&255,e[i+1]=r>>8,e[i+2]=e[i]^255,e[i+3]=e[i+1]^255;for(var o=0;o<r;++o)e[i+o+4]=n[o];return(i+4+r)*8},lt=function(e,t,n,r,i,o,l,a,c,f,u){X(t,u++,n),++i[256];for(var p=Ne(i,15),m=p.t,w=p.l,b=Ne(o,15),h=b.t,x=b.l,I=st(m),T=I.c,z=I.n,$=st(h),C=$.c,d=$.n,S=new R(19),v=0;v<T.length;++v)++S[T[v]&31];for(var v=0;v<C.length;++v)++S[C[v]&31];for(var g=Ne(S,7),N=g.t,O=g.l,P=19;P>4&&!N[ot[P-1]];--P);var K=f+5<<3,M=fe(i,_)+fe(o,ye)+l,q=fe(i,m)+fe(o,h)+l+14+3*P+fe(S,N)+2*S[16]+3*S[17]+7*S[18];if(c>=0&&K<=M&&K<=q)return gt(t,u,e.subarray(c,c+f));var U,F,V,Q;if(X(t,u,1+(q<M)),u+=2,q<M){U=he(m,w,0),F=m,V=he(h,x,0),Q=h;var Le=he(N,O,0);X(t,u,z-257),X(t,u+5,d-1),X(t,u+10,P-4),u+=14;for(var v=0;v<P;++v)X(t,u+3*v,N[ot[v]]);u+=3*P;for(var B=[T,C],ae=0;ae<2;++ae)for(var ne=B[ae],v=0;v<ne.length;++v){var j=ne[v]&31;X(t,u,Le[j]),u+=N[j],j>15&&(X(t,u,ne[v]>>5&127),u+=ne[v]>>12)}}else U=rn,F=_,V=on,Q=ye;for(var v=0;v<a;++v){var E=r[v];if(E>255){var j=E>>18&31;ue(t,u,U[j+257]),u+=F[j+257],j>7&&(X(t,u,E>>23&31),u+=Oe[j]);var re=E&31;ue(t,u,V[re]),u+=Q[re],re>3&&(ue(t,u,E>>5&8191),u+=qe[re])}else ue(t,u,U[E]),u+=F[E]}return ue(t,u,U[256]),u+F[256]},sn=new Re([65540,131080,131088,131104,262176,1048704,1048832,2114560,2117632]),vt=new A(0),ln=function(e,t,n,r,i,o){var l=o.z||e.length,a=new A(r+l+5*(1+Math.ceil(l/7e3))+i),c=a.subarray(r,a.length-i),f=o.l,u=(o.r||0)&7;if(t){u&&(c[0]=o.r>>3);for(var p=sn[t-1],m=p>>13,w=p&8191,b=(1<<n)-1,h=o.p||new R(32768),x=o.h||new R(b+1),I=Math.ceil(n/3),T=2*I,z=function(Me){return(e[Me]^e[Me+1]<<I^e[Me+2]<<T)&b},$=new Re(25e3),C=new R(288),d=new R(32),S=0,v=0,g=o.i||0,N=0,O=o.w||0,P=0;g+2<l;++g){var K=z(g),M=g&32767,q=x[K];if(h[M]=q,x[K]=M,O<=g){var U=l-g;if((S>7e3||N>24576)&&(U>423||!f)){u=lt(e,c,0,$,C,d,v,N,P,g-P,u),N=S=v=0,P=g;for(var F=0;F<286;++F)C[F]=0;for(var F=0;F<30;++F)d[F]=0}var V=2,Q=0,Le=w,B=M-q&32767;if(U>2&&K==z(g-B))for(var ae=Math.min(m,U)-1,ne=Math.min(32767,g),j=Math.min(258,U);B<=ne&&--Le&&M!=q;){if(e[g+V]==e[g+V-B]){for(var E=0;E<j&&e[g+E]==e[g+E-B];++E);if(E>V){if(V=E,Q=B,E>ae)break;for(var re=Math.min(B,E-2),Xe=0,F=0;F<re;++F){var Ie=g-B+F&32767,Bt=h[Ie],Ze=Ie-Bt&32767;Ze>Xe&&(Xe=Ze,q=Ie)}}}M=q,q=h[M],B+=M-q&32767}if(Q){$[N++]=268435456|Ee[V]<<18|at[Q];var We=Ee[V]&31,Ye=at[Q]&31;v+=Oe[We]+qe[Ye],++C[257+We],++d[Ye],O=g+V,++S}else $[N++]=e[g],++C[e[g]]}}for(g=Math.max(g,O);g<l;++g)$[N++]=e[g],++C[e[g]];u=lt(e,c,f,$,C,d,v,N,P,g-P,u),f||(o.r=u&7|c[u/8|0]<<3,u-=7,o.h=x,o.p=h,o.i=g,o.w=O)}else{for(var g=o.w||0;g<l+f;g+=65535){var Ce=g+65535;Ce>=l&&(c[u/8|0]=f,Ce=l),u=gt(c,u+1,e.subarray(g,Ce))}o.i=l}return mt(a,0,r+dt(u)+i)},cn=(function(){for(var e=new Int32Array(256),t=0;t<256;++t){for(var n=t,r=9;--r;)n=(n&1&&-306674912)^n>>>1;e[t]=n}return e})(),un=function(){var e=-1;return{p:function(t){for(var n=e,r=0;r<t.length;++r)n=cn[n&255^t[r]]^n>>>8;e=n},d:function(){return~e}}};var fn=function(e,t,n,r,i){if(!i&&(i={l:1},t.dictionary)){var o=t.dictionary.subarray(-32768),l=new A(o.length+e.length);l.set(o),l.set(e,o.length),e=l,i.w=o.length}return ln(e,t.level==null?6:t.level,t.mem==null?i.l?Math.ceil(Math.max(8,Math.min(13,Math.log(e.length)))*1.5):20:12+t.mem,n,r,i)},yt=function(e,t){var n={};for(var r in e)n[r]=e[r];for(var r in t)n[r]=t[r];return n};var D=function(e,t,n){for(;n;++t)e[t]=n,n>>>=8};function hn(e,t){return fn(e,t||{},0,0)}var wt=function(e,t,n,r){for(var i in e){var o=e[i],l=t+i,a=r;Array.isArray(o)&&(a=yt(r,o[1]),o=o[0]),ArrayBuffer.isView(o)?n[l]=[o,a]:(n[l+="/"]=[new A(0),a],wt(o,l,n,r))}},ct=typeof TextEncoder<"u"&&new TextEncoder,pn=typeof TextDecoder<"u"&&new TextDecoder,dn=0;try{pn.decode(vt,{stream:!0}),dn=1}catch{}function Z(e,t){if(t){for(var n=new A(e.length),r=0;r<e.length;++r)n[r]=e.charCodeAt(r);return n}if(ct)return ct.encode(e);for(var i=e.length,o=new A(e.length+(e.length>>1)),l=0,a=function(u){o[l++]=u},r=0;r<i;++r){if(l+5>o.length){var c=new A(l+8+(i-r<<1));c.set(o),o=c}var f=e.charCodeAt(r);f<128||t?a(f):f<2048?(a(192|f>>6),a(128|f&63)):f>55295&&f<57344?(f=65536+(f&1047552)|e.charCodeAt(++r)&1023,a(240|f>>18),a(128|f>>12&63),a(128|f>>6&63),a(128|f&63)):(a(224|f>>12),a(128|f>>6&63),a(128|f&63))}return mt(o,0,l)}var Pe=function(e){var t=0;if(e)for(var n in e){var r=e[n].length;r>65535&&we(9),t+=r+4}return t},ut=function(e,t,n,r,i,o,l,a){var c=r.length,f=n.extra,u=a&&a.length,p=Pe(f);D(e,t,l!=null?33639248:67324752),t+=4,l!=null&&(e[t++]=20,e[t++]=n.os),e[t]=20,t+=2,e[t++]=n.flag<<1|(o<0&&8),e[t++]=i&&8,e[t++]=n.compression&255,e[t++]=n.compression>>8;var m=new Date(n.mtime==null?Date.now():n.mtime),w=m.getFullYear()-1980;if((w<0||w>119)&&we(10),D(e,t,w<<25|m.getMonth()+1<<21|m.getDate()<<16|m.getHours()<<11|m.getMinutes()<<5|m.getSeconds()>>1),t+=4,o!=-1&&(D(e,t,n.crc),D(e,t+4,o<0?-o-2:o),D(e,t+8,n.size)),D(e,t+12,c),D(e,t+14,p),t+=16,l!=null&&(D(e,t,u),D(e,t+6,n.attrs),D(e,t+10,l),t+=14),e.set(r,t),t+=c,p)for(var b in f){var h=f[b],x=h.length;D(e,t,+b),D(e,t+2,x),e.set(h,t+4),t+=4+x}return u&&(e.set(a,t),t+=u),t},mn=function(e,t,n,r,i){D(e,t,101010256),D(e,t+8,n),D(e,t+10,n),D(e,t+12,r),D(e,t+16,i)};function bt(e,t){t||(t={});var n={},r=[];wt(e,"",n,t);var i=0,o=0;for(var l in n){var a=n[l],c=a[0],f=a[1],u=f.level==0?0:8,p=Z(l),m=p.length,w=f.comment,b=w&&Z(w),h=b&&b.length,x=Pe(f.extra);m>65535&&we(11);var I=u?hn(c,f):c,T=I.length,z=un();z.p(c),r.push(yt(f,{size:c.length,crc:z.d(),c:I,f:p,m:b,u:m!=l.length||b&&w.length!=h,o:i,compression:u})),i+=30+m+x+T,o+=76+2*(m+x)+(h||0)+T}for(var $=new A(o+22),C=i,d=o-i,S=0;S<r.length;++S){var p=r[S];ut($,p.o,p,p.f,p.u,p.c.length);var v=30+p.f.length+Pe(p.extra);$.set(p.c,p.o+v),ut($,i,p,p.f,p.u,p.c.length,p.o,p.m),i+=16+v+(p.m?p.m.length:0)}return mn($,i,r.length,d,C),$}function Tt(e){return e.replace(/[^\x09\x0A\x0D\x20--�]/g,"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function Ve(e){let t=e+1,n="";for(;t>0;){let r=(t-1)%26;n=String.fromCharCode(65+r)+n,t=Math.floor((t-1)/26)}return n}function gn(e){let t=new Set;return e.map((n,r)=>{let i=(n.replace(/[\\/?*[\]:]/g," ").replace(/^'+|'+$/g,"").trim()||`Sheet${r+1}`).slice(0,31),o=i;for(let l=2;t.has(o.toLowerCase());l++){let a=` (${l})`;o=i.slice(0,31-a.length)+a}return t.add(o.toLowerCase()),o})}function xt(e,t,n){if(typeof t=="number"&&Number.isFinite(t))return`<c r="${e}" s="${n}"><v>${t}</v></c>`;let r=String(t??"");return r?`<c r="${e}" s="${n}" t="inlineStr"><is><t xml:space="preserve">${Tt(r.slice(0,32767))}</t></is></c>`:`<c r="${e}" s="${n}"/>`}function vn(e){let t=Ve(Math.max(e.headers.length,1)-1),n=e.widths.map((l,a)=>`<col min="${a+1}" max="${a+1}" width="${Math.min(Math.max(l,4),80)+1}" customWidth="1"/>`).join(""),r=`<row r="1">${e.headers.map((l,a)=>xt(`${Ve(a)}1`,l,1)).join("")}</row>`,i=e.rows.map((l,a)=>`<row r="${a+2}">${l.map((c,f)=>xt(`${Ve(f)}${a+2}`,c,2)).join("")}</row>`).join(""),o=e.filter===!1?"":`<autoFilter ref="A1:${t}${e.rows.length+1}"/>`;return'<?xml version="1.0" encoding="UTF-8" standalone="yes"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetViews><sheetView workbookViewId="0"><pane ySplit="1" topLeftCell="A2" activePane="bottomLeft" state="frozen"/></sheetView></sheetViews><sheetFormatPr defaultRowHeight="15"/>'+(n?`<cols>${n}</cols>`:"")+`<sheetData>${r}${i}</sheetData>`+o+"</worksheet>"}var yn='<?xml version="1.0" encoding="UTF-8" standalone="yes"?><styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><fonts count="2"><font><sz val="11"/><name val="Calibri"/></font><font><b/><sz val="11"/><color rgb="FFFFFFFF"/><name val="Calibri"/></font></fonts><fills count="3"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill><fill><patternFill patternType="solid"><fgColor rgb="FF1F4E5F"/><bgColor indexed="64"/></patternFill></fill></fills><borders count="2"><border><left/><right/><top/><bottom/><diagonal/></border><border><left style="thin"><color rgb="FFD0D7DE"/></left><right style="thin"><color rgb="FFD0D7DE"/></right><top style="thin"><color rgb="FFD0D7DE"/></top><bottom style="thin"><color rgb="FFD0D7DE"/></bottom><diagonal/></border></borders><cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs><cellXfs count="3"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/><xf numFmtId="0" fontId="1" fillId="2" borderId="1" xfId="0" applyFont="1" applyFill="1" applyBorder="1" applyAlignment="1"><alignment vertical="center" wrapText="1"/></xf><xf numFmtId="0" fontId="0" fillId="0" borderId="1" xfId="0" applyBorder="1" applyAlignment="1"><alignment vertical="top" wrapText="1"/></xf></cellXfs><cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles></styleSheet>';function kt(e){let t=gn(e.map(i=>i.name)),n={},r='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';return n["[Content_Types].xml"]=Z(`${r}<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>`+e.map((i,o)=>`<Override PartName="/xl/worksheets/sheet${o+1}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>`).join("")+"</Types>"),n["_rels/.rels"]=Z(`${r}<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>`),n["xl/workbook.xml"]=Z(`${r}<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheets>${t.map((i,o)=>`<sheet name="${Tt(i)}" sheetId="${o+1}" r:id="rId${o+1}"/>`).join("")}</sheets></workbook>`),n["xl/_rels/workbook.xml.rels"]=Z(`${r}<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">`+e.map((i,o)=>`<Relationship Id="rId${o+1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet${o+1}.xml"/>`).join("")+`<Relationship Id="rId${e.length+1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/></Relationships>`),n["xl/styles.xml"]=Z(yn),e.forEach((i,o)=>{n[`xl/worksheets/sheet${o+1}.xml`]=Z(vn(i))}),bt(n,{level:6})}function Ue(e,t=e.rows){return t.map(n=>e.columns.map(r=>n.values[r.key]??""))}function $t(e,t){let n=r=>{let i=String(r);return i.includes(t)||/["\r\n]/.test(i)?`"${i.replace(/"/g,'""')}"`:i};return[e.columns.map(r=>r.header),...Ue(e)].map(r=>r.map(n).join(t)).join(`\r
|
|
3
|
+
`)}function St(e){return"\uFEFF"+$t(e,",")}function Lt(e){return $t(e," ")}function It(e){return JSON.stringify({title:e.title,source:Object.fromEntries(e.meta),fields:e.rows.map(t=>Object.fromEntries(e.columns.map(n=>[n.header,t.values[n.key]??""]))),choices:e.choices},null,2)}function Ct(e){let t=i=>String(i).replace(/\\/g,"\\\\").replace(/\|/g,"\\|").replace(/\r?\n/g,"<br>"),n=e.columns.filter(i=>i.key!=="tab"),r=[`# ${e.title}`,""];for(let[i,o]of e.meta)r.push(`- **${i}:** ${o}`);for(let i of e.tabs){let o=e.rows.filter(l=>l.groupKey===i.key);if(o.length){r.push("",`## ${i.label}`,""),r.push(`| ${n.map(l=>l.header).join(" | ")} |`),r.push(`| ${n.map(()=>"---").join(" | ")} |`);for(let l of o)r.push(`| ${n.map(a=>t(l.values[a.key]??"")).join(" | ")} |`)}}return r.join(`
|
|
4
|
+
`)+`
|
|
5
|
+
`}function Mt(e){let t=e.columns.map(i=>i.header),n=e.columns.map(i=>i.width),r=[];if(e.splitByTab&&e.tabs.length>1)for(let i of e.tabs){let o=e.rows.filter(l=>l.groupKey===i.key);o.length&&r.push({name:i.label,headers:t,widths:n,rows:Ue(e,o)})}else r.push({name:"Data Dictionary",headers:t,widths:n,rows:Ue(e)});return e.choices.length&&r.push({name:"Allowed Values",headers:["Table","Logical Name","Label","Value","Choice Label"],widths:[18,26,26,12,34],rows:e.choices.map(i=>[i.table,i.logicalName,i.label,i.value,i.choice])}),r.push({name:"About",headers:["Property","Value"],widths:[22,80],rows:e.meta.map(([i,o])=>[i,o]),filter:!1}),kt(r)}function Ft(e){return e.normalize("NFKD").replace(/[^\w\s-]/g,"").trim().replace(/[\s_]+/g,"-").toLowerCase().slice(0,60)||"data-dictionary"}var wn={"e7a81278-8635-4d9e-8d4d-59480b391c5b":"Subgrid","5c5600e0-1d6e-4205-a272-be80da87fd42":"Quick view","9fdf5f91-88b1-47f4-ad53-c11efc01a01d":"Web resource","fd2a7985-3187-444e-908d-6624b21f69c0":"IFrame","06375649-c143-495e-a496-c962e5b4488e":"Timeline"};function ee(e,t){let n=[];if(!e)return n;let r=e.childNodes;for(let i=0;i<r.length;i++){let o=r[i];o.nodeType===1&&o.nodeName.toLowerCase()===t&&n.push(o)}return n}function W(e,t){return ee(e,t)[0]}function Y(e,t){return e?.getAttribute(t)??""}function be(e){return Y(e,"visible").toLowerCase()!=="false"}function Be(e,t){let n={};for(let r of ee(W(e,"labels"),"label")){let i=parseInt(Y(r,"languagecode"),10),o=Y(r,"description");Number.isFinite(i)&&(n[i]=o,t?.set(i,(t.get(i)??0)+1))}return n}function xe(e,t){let n=e?.getElementsByTagName(t);return n&&n.length?(n[0].textContent??"").trim():""}function bn(e,t){let n=W(t,"parameters");if(!n)return"";if(e==="Subgrid"){let r=xe(n,"TargetEntityType"),i=xe(n,"RelationshipName");return[r&&`Rows from ${r}`,i&&`via ${i}`].filter(Boolean).join(" ")}return e==="Web resource"||e==="IFrame"?xe(n,"Url"):""}function Dt(e,t){let r=t(e).documentElement;if(!r||r.nodeName.toLowerCase()!=="form")throw new Error("This form's definition could not be read (unexpected form XML).");let i=[],o=new Map,l=(p,m)=>{for(let w of ee(W(p,"rows"),"row"))for(let b of ee(w,"cell")){let h=W(b,"control");if(!h)continue;let x=Y(h,"datafieldname").trim().toLowerCase(),I=Y(h,"classid").replace(/[{}]/g,"").toLowerCase(),T=x?"Field":wn[I]??"Other control";i.push({...m,cellLabels:Be(b,o),cellVisible:be(b)&&be(h),controlId:Y(h,"id"),controlType:T,fieldName:x||null,table:"",disabled:Y(h,"disabled").toLowerCase()==="true",detail:x?"":bn(T,h)})}},a=p=>({area:p,groupKey:p.toLowerCase(),tabName:p.toLowerCase(),tabLabels:{},tabVisible:!0,sectionName:"",sectionLabels:{},sectionVisible:!0}),c=W(r,"header");c&&l(c,a("Header")),ee(W(r,"tabs"),"tab").forEach((p,m)=>{let w=Be(p,o);for(let b of ee(W(p,"columns"),"column"))for(let h of ee(W(b,"sections"),"section"))l(h,{area:"Body",groupKey:`tab:${m}`,tabName:Y(p,"name")||`tab_${m+1}`,tabLabels:w,tabVisible:be(p),sectionName:Y(h,"name"),sectionLabels:Be(h,o),sectionVisible:be(h)})});let f=W(r,"footer");f&&l(f,a("Footer"));let u=[...o.entries()].sort((p,m)=>m[1]-p[1]).map(([p])=>p);return{items:i,languages:u}}function xn(e,t){let n={};if(!e)return n;let r=t(e).getElementsByTagName("link-entity");for(let i=0;i<r.length;i++){let o=r[i].getAttribute("alias"),l=r[i].getAttribute("name");o&&l&&(n[o.toLowerCase()]=l.toLowerCase())}return n}function Nt(e,t,n){let i=n(e).getElementsByTagName("cell"),o=xn(t,n),l=[];for(let a=0;a<i.length;a++){let c=i[a],f=(c.getAttribute("name")??"").trim().toLowerCase();if(!f)continue;let u=f.indexOf("."),p=u>0?f.slice(0,u):"",m=c.getAttribute("width");l.push({area:"View",groupKey:"view",tabName:"view",tabLabels:{},tabVisible:!0,sectionName:"",sectionLabels:{},sectionVisible:!0,cellLabels:{},cellVisible:(c.getAttribute("ishidden")??"0")!=="1",controlId:f,controlType:"Field",fieldName:u>0?f.slice(u+1):f,table:p?o[p]??`?${p}`:"",disabled:!1,detail:m?`Column width ${m}px`:""})}return{items:l,languages:[]}}var Tn={1030:"Danish",1031:"German",1033:"English",1035:"Finnish",1036:"French",1040:"Italian",1041:"Japanese",1043:"Dutch",1044:"Norwegian",1045:"Polish",1046:"Portuguese (Brazil)",1053:"Swedish",2052:"Chinese (Simplified)",2070:"Portuguese",3082:"Spanish"},s={connection:null,target:null,notices:[],pickerOptions:null,pickerTable:"",surface:null,layout:null,tables:new Map,dict:null,options:{includeHidden:!1,includeControls:!1,uniqueOnly:!1,splitByTab:!1},lcid:1033,columns:new Set(le.filter(e=>e.on).map(e=>e.key)),tabFilter:null,search:"",busy:!1,runId:0},k=e=>document.getElementById(e);function L(e){return String(e??"").replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function Se(e){return(e instanceof Error?e.message:String(e)).replace(/^Error invoking remote method '[^']+': (Error: )?/,"")}async function Rt(e,t,n="success"){try{await window.toolboxAPI.utils.showNotification({title:e,body:t,type:n,duration:4e3})}catch{}}async function He(){try{let t=await window.toolboxAPI.connections.getActiveConnection();s.connection=t?{name:t.name,url:t.url}:null}catch{s.connection=null}let e=k("conn");e.classList.toggle("missing",!s.connection),e.innerHTML=s.connection?`<strong>${L(s.connection.name)}</strong> \xB7 ${L(s.connection.url.replace(/^https?:\/\//,"").replace(/\/$/,""))}`:"No connection. Select one in ToolBox, then build."}async function Et(){try{document.documentElement.dataset.theme=await window.toolboxAPI.utils.getCurrentTheme()}catch{document.documentElement.dataset.theme="light"}}async function kn(){try{let e=await window.toolboxAPI.settings.getAll();if(Array.isArray(e?.columns)){let t=new Set(le.map(n=>n.key));s.columns=new Set(e.columns.filter(n=>t.has(n)))}e?.options&&typeof e.options=="object"&&(s.options={...s.options,...e.options})}catch{}}function At(){window.toolboxAPI.settings.setAll({columns:[...s.columns],options:s.options}).catch(()=>{})}function $e(e,t="Build dictionary"){s.busy=e;let n=k("build");n.disabled=e,n.textContent=t}function Ot(){s.surface=null,s.layout=null,s.dict=null,s.tabFilter=null,s.search="",k("search").value=""}async function zt(){if(s.busy)return;let e=++s.runId,t=_e(k("url").value);if(s.target=t,s.notices=[],s.pickerOptions=null,Ot(),t.kind==="unknown")return s.notices.push({level:"error",text:"That address does not name a form, view or table. Open the record or view in your app and copy the address from the browser, or enter a table name such as account."}),H();$e(!0,"Reading\u2026"),H();try{if(await He(),!s.connection)throw new Error("There is no active connection. Select a connection for this tool in ToolBox and try again.");let n=et(t,s.connection.url);n&&s.notices.push({level:"warning",text:n});let r=null;if(t.kind==="form"?r=await ge(t.formId):t.kind==="view"?r=await ve(t.viewId):t.kind==="id"&&(r=await ge(t.anyId)??await ve(t.anyId)),e!==s.runId)return;if(r)await qt(r,e);else if(t.etn)t.kind!=="table"&&s.notices.push({level:"warning",text:`The ${t.kind} in that address was not found in this environment. Choose a form or view of ${t.etn} instead.`}),await $n(t.etn,t.appId,e);else throw new Error("No form or view with that id exists in the connected environment. Check that the active connection is the environment the address came from.")}catch(n){e===s.runId&&s.notices.push({level:"error",text:Se(n)})}finally{e===s.runId&&($e(!1),H())}}async function $n(e,t,n){let r=await rt(e,t);if(n===s.runId){if(!r.length)throw new Error(`No active forms or views were found for the table "${e}". Check the table name and the active connection.`);s.pickerOptions=r,s.pickerTable=e}}async function Sn(e){if(s.busy)return;let t=++s.runId;s.notices=s.notices.filter(n=>n.level==="warning"&&n.text.startsWith("The URL points")),$e(!0,"Reading\u2026");try{let n=e.kind==="form"?await ge(e.id):await ve(e.id);if(!n)throw new Error(`"${e.name}" could not be read. It may have been deleted, or you may not have access to it.`);t===s.runId&&await qt(n,t)}catch(n){t===s.runId&&s.notices.push({level:"error",text:Se(n)})}finally{t===s.runId&&($e(!1),H())}}async function qt(e,t){let n=a=>new DOMParser().parseFromString(a,"text/xml"),r=e.kind==="form"?Dt(e.xml,n):Nt(e.xml,e.fetchXml,n),i=r.languages[0]??1033,o=new Set([e.etn]);for(let a of r.items)a.table&&!a.table.startsWith("?")&&o.add(a.table);let l=new Map;await Promise.all([...o].map(async a=>{try{l.set(a,await it(a,i))}catch(c){if(a===e.etn)throw c;s.notices.push({level:"warning",text:`Column details for the linked table "${a}" could not be read: ${Se(c)}`})}})),t===s.runId&&(s.surface=e,s.layout=r,s.tables=l,s.lcid=i,s.tabFilter=null,Ke(),s.dict?.multipleTables&&s.columns.add("table"))}function Ke(){!s.surface||!s.layout||(s.dict=Qe(s.layout.items,s.surface.etn,s.tables,{...s.options,lcid:s.lcid}),s.tabFilter&&!s.dict.tabs.some(e=>e.key===s.tabFilter)&&(s.tabFilter=null))}function Ge(){return le.filter(e=>s.columns.has(e.key))}function Vt(){if(!s.dict)return[];let e=s.search.trim().toLowerCase(),t=Ge();return s.dict.rows.filter(n=>s.tabFilter&&n.groupKey!==s.tabFilter?!1:e?t.some(r=>String(n.values[r.key]??"").toLowerCase().includes(e))||String(n.values.logicalName).toLowerCase().includes(e):!0)}function Je(){let e=s.surface;if(!e)return"";let t=s.tables.get(e.etn);return t&&t.displayName!==e.etn?`${t.displayName} (${e.etn})`:e.etn}function Ln(){let e=s.surface,t=s.dict,n=Vt(),r=new Set(n.map(o=>o.groupKey)),i=[s.options.includeHidden?"hidden items included":"hidden items left out",s.options.includeControls?"subgrids and other controls included":"fields only",s.options.uniqueOnly?"first occurrence of each field only":"",s.tabFilter?"one tab only":"",s.search.trim()?`filtered by "${s.search.trim()}"`:""].filter(Boolean);return{title:`Data dictionary: ${Je()}, ${e.name}`,meta:[["Table",Je()],[e.typeLabel,e.name],["Source address",s.target?.raw??""],["Environment",s.connection?`${s.connection.name} (${s.connection.url})`:""],["Generated",new Date().toLocaleString()],["Rows",String(n.length)],["Scope",i.join("; ")],["Note","Reflects the saved design. Business rules, scripts and security roles can show, hide or lock fields when a user opens it."]],columns:Ge(),rows:n,tabs:t.tabs.filter(o=>r.has(o.key)),choices:t.choices.filter(o=>n.some(l=>l.values.logicalName===o.logicalName)),splitByTab:s.options.splitByTab}}async function Te(e,t,n,r){let i=window.toolboxAPI,o=i.fileSystem?.saveFile??i.utils?.saveFile;if(o){let c=await o(e,t,[{name:n,extensions:[r]}]);c&&await Rt("Data dictionary saved",String(c));return}let l=new Blob([t]),a=document.createElement("a");a.href=URL.createObjectURL(l),a.download=e,a.click(),URL.revokeObjectURL(a.href)}async function In(e){if(!s.surface||!s.dict)return;let t=Ln();if(!t.rows.length)return s.notices=[{level:"warning",text:"There are no rows to export with the current filters."}],H();let n=`${Ft(`${s.surface.etn} ${s.surface.name}`)}-data-dictionary`;try{e==="xlsx"?await Te(`${n}.xlsx`,Mt(t),"Excel workbook","xlsx"):e==="csv"?await Te(`${n}.csv`,St(t),"CSV","csv"):e==="md"?await Te(`${n}.md`,Ct(t),"Markdown","md"):e==="json"?await Te(`${n}.json`,It(t),"JSON","json"):e==="copy"&&(await window.toolboxAPI.utils.copyToClipboard(Lt(t)),await Rt("Copied",`${t.rows.length} rows are on the clipboard. Paste into Excel or a document.`))}catch(r){s.notices=[{level:"error",text:`The export did not complete: ${Se(r)}`}],H()}}function Cn(){k("notices").innerHTML=s.notices.map(e=>`<div class="notice ${e.level}" role="${e.level==="error"?"alert":"status"}">${L(e.text)}</div>`).join("")}function Mn(){let e=s.pickerOptions??[],t=s.target?.prefer??"form";k("picker-title").textContent=`Choose a form or view of ${s.pickerTable}`,k("picker-hint").textContent="The address names the table but not which form or view. People can see different forms depending on their security roles and the form they used last, so pick the one you want to document.";let n=(o,l)=>{let a=e.filter(c=>c.kind===o).sort((c,f)=>Number(f.inApp)-Number(c.inApp));return a.length?`<h3>${l}</h3>`+a.map(c=>`<button type="button" class="pick" data-pick="${L(c.id)}"><span class="name">${L(c.name)}</span><span class="badge">${L(c.typeLabel)}</span>`+(c.inApp?'<span class="badge app">In this app</span>':"")+(c.isDefault?'<span class="badge">Default view</span>':"")+`<span class="desc">${L(c.description)}</span></button>`).join(""):""},r=n("form","Forms"),i=n("view","Views");k("picker-list").innerHTML=t==="view"?i+r:r+i}function te(e,t,n=`${t}s`){return`${e} ${e===1?t:n}`}function Fn(){let e=s.surface,t=s.dict,n=t.rows.filter(o=>o.isField).length,r=t.tabs.filter(o=>o.key.startsWith("tab:")).length,i=[te(n,"field")];e.kind==="form"&&i.push(te(r,"tab")),t.rows.length>n&&i.push(te(t.rows.length-n,"other control")),k("summary").innerHTML=`<span class="title">${L(e.name)}</span><span class="meta">${L(e.typeLabel)} \xB7 ${L(Je())} \xB7 ${i.join(" \xB7 ")}</span>`+(s.pickerOptions?'<button type="button" class="linklike" id="back-to-picker">Choose another form or view</button>':"")}function Ut(){let e=s.dict;if(e.tabs.length<2){k("tabs").innerHTML="";return}let t=(n,r,i)=>`<button type="button" class="chip" data-tab="${L(n??"")}" aria-pressed="${s.tabFilter===n}">${L(r)}<span class="n">${i}</span></button>`;k("tabs").innerHTML=t(null,"All",e.rows.length)+e.tabs.map(n=>t(n.key,n.label,n.count)).join("")}function Dn(){let e=s.options,t=s.surface?.kind==="form",n=(i,o)=>`<label><input type="checkbox" data-option="${i}" ${e[i]?"checked":""} /> ${o}</label>`,r=s.layout?.languages??[];k("options-panel").innerHTML=n("includeHidden",t?"Include hidden tabs, sections and fields":"Include hidden columns")+(t?n("includeControls","Include subgrids, quick views and web resources"):"")+n("uniqueOnly","List each field once (first appearance)")+(t?n("splitByTab","Excel: one worksheet per tab"):"")+(r.length>1?`<div class="field"><span>Label language</span><select id="language">${r.map(i=>`<option value="${i}" ${i===s.lcid?"selected":""}>${L(Tn[i]??`Language ${i}`)}</option>`).join("")}</select></div>`:"")}function Nn(){k("columns-panel").innerHTML=le.map(e=>`<label><input type="checkbox" data-column="${e.key}" ${s.columns.has(e.key)?"checked":""} /> ${L(e.header==="#"?"# (order)":e.header)}${e.key==="tab"?" (exports)":""}</label>`).join("")}function En(e,t){let n=String(t??"");if(e.key==="choices"){let r=n.split(`
|
|
6
|
+
`);if(r.length>6)return`<span title="${L(n)}">${L(r.slice(0,5).join(`
|
|
7
|
+
`))}
|
|
8
|
+
<span class="more">+ ${r.length-5} more (all are exported)</span></span>`}return L(n)}function ke(){let e=s.dict,t=Ge().filter(a=>a.key!=="tab"),n=Vt(),r=`<thead><tr>${t.map(a=>`<th style="min-width:${Math.min(a.width,30)*7+20}px">${L(a.header)}</th>`).join("")}</tr></thead>`,i="",o="";for(let a of n){if(a.groupKey!==o){o=a.groupKey;let c=e.tabs.find(u=>u.key===a.groupKey),f=n.filter(u=>u.groupKey===a.groupKey).length;i+=`<tr class="group"><td colspan="${t.length}">${L(c?.label??"")}<span class="n">${f}</span></td></tr>`}i+=`<tr class="${a.hidden?"is-hidden":""}">${t.map(c=>`<td class="c-${c.key}${c.mono?" mono":""}">${En(c,a.values[c.key])}</td>`).join("")}</tr>`}if(!n.length){let a=e.rows.length?"No fields match the current filter.":"Nothing to list. If everything on this form is hidden, turn on \u201CInclude hidden\u201D under Options.";i=`<tr class="none"><td colspan="${Math.max(t.length,1)}">${a}</td></tr>`}k("grid").innerHTML=`${r}<tbody>${i}</tbody>`;let l=[];n.length!==e.rows.length&&l.push(`Showing ${n.length} of ${e.rows.length} rows; exports contain the rows shown.`),e.hiddenSkipped&&l.push(`${te(e.hiddenSkipped,"hidden item")} left out.`),e.controlsSkipped&&l.push(`${te(e.controlsSkipped,"subgrid or other non-field control")} left out.`),e.repeatsSkipped&&l.push(`${te(e.repeatsSkipped,"repeat appearance")} left out.`),e.missingMetadata&&l.push(`${te(e.missingMetadata,"field")} without column details; turn on the Notes column to see why.`),(e.hiddenSkipped||e.controlsSkipped)&&l.push("Change this under Options."),k("footnote").textContent=l.join(" ")}function H(){Cn();let e=!!(s.surface&&s.dict),t=!e&&!!s.pickerOptions;k("picker").hidden=!t,k("result").hidden=!e,k("empty").hidden=e||t||s.busy||s.notices.some(n=>n.level==="error"),t&&Mn(),e&&(Fn(),Ut(),Dn(),Nn(),ke())}function je(e){document.querySelectorAll("details.menu[open]").forEach(t=>{t!==e&&(t.open=!1)})}function An(){k("build").addEventListener("click",()=>void zt()),k("url").addEventListener("keydown",e=>{e.key==="Enter"&&zt()}),k("search").addEventListener("input",e=>{s.search=e.target.value,ke()}),document.addEventListener("click",e=>{let t=e.target;je(t.closest("details.menu"));let n=t.closest("[data-pick]");if(n){let o=s.pickerOptions?.find(l=>l.id===n.dataset.pick);o&&Sn(o);return}if(t.id==="back-to-picker")return Ot(),H();let r=t.closest("[data-tab]");if(r)return s.tabFilter=r.dataset.tab||null,Ut(),ke();let i=t.closest("[data-export]");i&&(je(),In(i.dataset.export))}),document.addEventListener("change",e=>{let t=e.target;if(t.dataset.option){let n=t.dataset.option;s.options[n]=t.checked,n==="includeHidden"&&t.checked&&s.columns.add("visible"),n==="includeControls"&&t.checked&&s.columns.add("notes"),At(),Ke(),H()}else t.dataset.column?(t.checked?s.columns.add(t.dataset.column):s.columns.delete(t.dataset.column),At(),ke()):t.id==="language"&&(s.lcid=Number(t.value),Ke(),H())}),document.addEventListener("keydown",e=>{e.key==="Escape"&&je()})}async function Pt(){An(),await Promise.all([Et(),He(),kn()]);try{window.toolboxAPI.events.on((e,t)=>{t?.event?.startsWith("connection:")&&He(),t?.event==="settings:updated"&&Et()})}catch{}H(),k("url").focus()}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>void Pt()):Pt();})();
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
|
2
|
+
<path d="M5 3.5h11a2.5 2.5 0 0 1 2.5 2.5v14.5H7A2 2 0 0 1 5 18.5z"/>
|
|
3
|
+
<path d="M5 18.5a2 2 0 0 1 2-2h11.5"/>
|
|
4
|
+
<path d="M8.5 7.5h1M11.5 7.5h4M8.5 10.5h1M11.5 10.5h4M8.5 13.5h1M11.5 13.5h4"/>
|
|
5
|
+
</svg>
|
package/dist/index.html
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Data Dictionary Builder</title>
|
|
7
|
+
<link rel="stylesheet" href="styles.css" />
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="app">
|
|
11
|
+
<header class="top">
|
|
12
|
+
<h1>Data Dictionary Builder</h1>
|
|
13
|
+
<div id="conn" class="conn" title="Active connection"></div>
|
|
14
|
+
</header>
|
|
15
|
+
|
|
16
|
+
<section class="source">
|
|
17
|
+
<label for="url">Form or view address</label>
|
|
18
|
+
<div class="source-row">
|
|
19
|
+
<input id="url" type="text" spellcheck="false" autocomplete="off" placeholder="https://yourorg.crm.dynamics.com/main.aspx?appid=…&pagetype=entityrecord&etn=account&id=…" />
|
|
20
|
+
<button id="build" class="btn primary" type="button">Build dictionary</button>
|
|
21
|
+
</div>
|
|
22
|
+
<p class="hint">
|
|
23
|
+
Paste the address of an open record or view from a model-driven app, or of a form in the make.powerapps.com designer. A table name such as <code>account</code> works too.
|
|
24
|
+
</p>
|
|
25
|
+
</section>
|
|
26
|
+
|
|
27
|
+
<div id="notices"></div>
|
|
28
|
+
|
|
29
|
+
<section id="picker" class="picker" hidden>
|
|
30
|
+
<h2 id="picker-title"></h2>
|
|
31
|
+
<p id="picker-hint" class="hint"></p>
|
|
32
|
+
<div id="picker-list"></div>
|
|
33
|
+
</section>
|
|
34
|
+
|
|
35
|
+
<section id="result" hidden>
|
|
36
|
+
<div id="summary" class="summary"></div>
|
|
37
|
+
<div class="toolbar">
|
|
38
|
+
<div id="tabs" class="chips" role="group" aria-label="Filter by tab"></div>
|
|
39
|
+
<div class="tools">
|
|
40
|
+
<input id="search" type="search" placeholder="Filter fields" aria-label="Filter fields" />
|
|
41
|
+
<details class="menu" id="options-menu">
|
|
42
|
+
<summary class="btn">Options</summary>
|
|
43
|
+
<div class="menu-panel" id="options-panel"></div>
|
|
44
|
+
</details>
|
|
45
|
+
<details class="menu" id="columns-menu">
|
|
46
|
+
<summary class="btn">Columns</summary>
|
|
47
|
+
<div class="menu-panel menu-columns" id="columns-panel"></div>
|
|
48
|
+
</details>
|
|
49
|
+
<details class="menu" id="export-menu">
|
|
50
|
+
<summary class="btn primary">Export</summary>
|
|
51
|
+
<div class="menu-panel menu-actions">
|
|
52
|
+
<button type="button" data-export="xlsx">Excel workbook (.xlsx)</button>
|
|
53
|
+
<button type="button" data-export="csv">CSV</button>
|
|
54
|
+
<button type="button" data-export="md">Markdown</button>
|
|
55
|
+
<button type="button" data-export="json">JSON</button>
|
|
56
|
+
<hr />
|
|
57
|
+
<button type="button" data-export="copy">Copy table to clipboard</button>
|
|
58
|
+
</div>
|
|
59
|
+
</details>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="table-wrap">
|
|
63
|
+
<table id="grid"></table>
|
|
64
|
+
</div>
|
|
65
|
+
<p id="footnote" class="footnote"></p>
|
|
66
|
+
</section>
|
|
67
|
+
|
|
68
|
+
<section id="empty" class="empty">
|
|
69
|
+
<h2>What you get</h2>
|
|
70
|
+
<ul>
|
|
71
|
+
<li>Every field on the form or view, in the order it appears on screen: header, then each tab and section, then footer.</li>
|
|
72
|
+
<li>Data dictionary columns for each field: label, logical name, data type, format, length or range, required level, allowed values, lookup targets and description.</li>
|
|
73
|
+
<li>Export to Excel, CSV, Markdown or JSON, or copy the table straight into a document.</li>
|
|
74
|
+
</ul>
|
|
75
|
+
<p class="hint">The tool only reads form, view and column definitions. It does not read or change any records.</p>
|
|
76
|
+
</section>
|
|
77
|
+
</div>
|
|
78
|
+
<script src="app.js"></script>
|
|
79
|
+
</body>
|
|
80
|
+
</html>
|