canada-api 1.1.6 → 2.0.3
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/README.md +22 -73
- package/dist/ca.js +1 -1
- package/package.json +8 -7
- package/src/children.js +47 -0
- package/src/content.js +42 -0
- package/src/fetch.js +60 -0
- package/src/index.js +16 -0
- package/src/meta.js +84 -0
- package/src/normalize.js +120 -0
- package/test.html +8 -0
- package/webpack.config.js +2 -2
- package/src/index.mjs +0 -204
package/README.md
CHANGED
|
@@ -5,88 +5,37 @@ Cross platform API for fetching public data from [canada.ca](https://www.canada.
|
|
|
5
5
|
## Install
|
|
6
6
|
### Browsers
|
|
7
7
|
|
|
8
|
-
<script src="https://cdn.jsdelivr.net/npm/canada-api@
|
|
9
|
-
|
|
10
|
-
### Nodejs
|
|
11
|
-
|
|
12
|
-
npm install canada-api
|
|
13
|
-
|
|
14
|
-
## Usage
|
|
15
|
-
### Browsers
|
|
16
|
-
|
|
8
|
+
<script src="https://cdn.jsdelivr.net/npm/canada-api@2.0.3"></script>
|
|
17
9
|
<script>
|
|
18
|
-
ca.meta("en/department-national-defence")
|
|
10
|
+
ca.meta("en/department-national-defence").then(meta => {
|
|
11
|
+
console.log(meta)
|
|
12
|
+
})
|
|
19
13
|
</script>
|
|
20
14
|
|
|
21
15
|
### Nodejs
|
|
22
16
|
|
|
17
|
+
npm install canada-api
|
|
18
|
+
|
|
23
19
|
const ca = require("canada-api")
|
|
24
20
|
ca.meta("en/department-national-defence")
|
|
25
21
|
|
|
26
|
-
## API
|
|
27
|
-
### ca.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
Returns: `Promise` Resolves: node object with `children` property
|
|
35
|
-
|
|
36
|
-
Only for page nodes, other types return an error `Not Found`
|
|
37
|
-
|
|
38
|
-
### ca.meta(node)
|
|
39
|
-
|
|
40
|
-
Fetches metadata of a node
|
|
41
|
-
|
|
42
|
-
ca.meta("/en/department-national-defence/maple-leaf")
|
|
43
|
-
ca.meta("/content/dam/dnd-mdn/images/maple-leaf/ml-logo.jpg")
|
|
44
|
-
|
|
45
|
-
Returns: `Promise` Resolves: node object with `meta` property
|
|
46
|
-
|
|
47
|
-
### ca.content(node)
|
|
48
|
-
|
|
49
|
-
Fetches content of a node, as text or json
|
|
50
|
-
|
|
51
|
-
ca.content("/en/department-national-defence/maple-leaf")
|
|
52
|
-
ca.content("/content/dam/dnd-mdn/documents/json/maple-en.json")
|
|
53
|
-
|
|
54
|
-
Returns: `Promise` Resolves: node object with `content` property
|
|
55
|
-
|
|
56
|
-
### ca.normalizeNode
|
|
57
|
-
### ca.normalizePath
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
## Arguments
|
|
61
|
-
|
|
62
|
-
All functions take a node argument which accepts a couple types
|
|
63
|
-
|
|
64
|
-
### path string
|
|
65
|
-
|
|
66
|
-
Valid path strings include:
|
|
67
|
-
|
|
68
|
-
en/department-national-defence
|
|
69
|
-
/en/department-national-defence.html
|
|
70
|
-
/en/department-national-defence?param=ignored
|
|
71
|
-
|
|
72
|
-
https://www.canada.ca/en/department-national-defence.html
|
|
73
|
-
|
|
74
|
-
content/canadasite/department-national-defence
|
|
75
|
-
/content/canadasite/department-national-defence/
|
|
76
|
-
|
|
77
|
-
content/dam/dnd-mdn/documents/json/maple-en.json
|
|
78
|
-
/content/dam/dnd-mdn/images/maple-leaf/ml-logo.jpg
|
|
22
|
+
## Core API
|
|
23
|
+
### ca.fetch(url, [options])
|
|
24
|
+
`Promise<Response>` Rate limited [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) with some error checking
|
|
25
|
+
### ca.fetch.limiter
|
|
26
|
+
`Bottleneck` Access to [rate limiter](https://www.npmjs.com/package/bottleneck)
|
|
27
|
+
### ca.normalize(url, [type=path])
|
|
28
|
+
`string` Normalize AEM url ('path', 'children', 'content', 'meta')
|
|
79
29
|
|
|
80
|
-
### node object
|
|
81
30
|
|
|
82
|
-
|
|
31
|
+
## Basic API
|
|
32
|
+
### ca.children(url, [options])
|
|
33
|
+
`Promise<Array>` Array of child nodes
|
|
34
|
+
### ca.content(url, [options])
|
|
35
|
+
`Promise<Object|string>` Content of a node, as text or json
|
|
36
|
+
### ca.meta(url, [options])
|
|
37
|
+
`Promise<Object>` Metadata object
|
|
83
38
|
|
|
84
|
-
var node = { path: '/en/department-national-defence/maple-leaf' }
|
|
85
39
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
ca.content(node)
|
|
89
|
-
]).then(function() {
|
|
90
|
-
// object exented with meta and content properties
|
|
91
|
-
console.log(node)
|
|
92
|
-
})
|
|
40
|
+
## Extended API
|
|
41
|
+
TBD
|
package/dist/ca.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("cross-fetch")):"function"==typeof define&&define.amd?define("ca",["cross-fetch"],e):"object"==typeof exports?exports.ca=e(require("cross-fetch")):t.ca=e(t.fetch)}("undefined"!=typeof self?self:this,(t=>(()=>{var e={209:function(t,e,s){t.exports=function(){"use strict";var t="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:void 0!==s.g?s.g:"undefined"!=typeof self?self:{};var e,r,i={load:function(t,e,s={}){var r,i,n;for(r in e)n=e[r],s[r]=null!=(i=t[r])?i:n;return s},overwrite:function(t,e,s={}){var r,i;for(r in t)i=t[r],void 0!==e[r]&&(s[r]=i);return s}},n=class{constructor(t,e){this.incr=t,this.decr=e,this._first=null,this._last=null,this.length=0}push(t){var e;this.length++,"function"==typeof this.incr&&this.incr(),e={value:t,prev:this._last,next:null},null!=this._last?(this._last.next=e,this._last=e):this._first=this._last=e}shift(){var t;if(null!=this._first)return this.length--,"function"==typeof this.decr&&this.decr(),t=this._first.value,null!=(this._first=this._first.next)?this._first.prev=null:this._last=null,t}first(){if(null!=this._first)return this._first.value}getArray(){var t,e,s;for(t=this._first,s=[];null!=t;)s.push((e=t,t=t.next,e.value));return s}forEachShift(t){var e;for(e=this.shift();null!=e;)t(e),e=this.shift()}debug(){var t,e,s,r,i;for(t=this._first,i=[];null!=t;)i.push((e=t,t=t.next,{value:e.value,prev:null!=(s=e.prev)?s.value:void 0,next:null!=(r=e.next)?r.value:void 0}));return i}},o=class{constructor(t){if(this.instance=t,this._events={},null!=this.instance.on||null!=this.instance.once||null!=this.instance.removeAllListeners)throw new Error("An Emitter already exists for this object");this.instance.on=(t,e)=>this._addListener(t,"many",e),this.instance.once=(t,e)=>this._addListener(t,"once",e),this.instance.removeAllListeners=(t=null)=>null!=t?delete this._events[t]:this._events={}}_addListener(t,e,s){var r;return null==(r=this._events)[t]&&(r[t]=[]),this._events[t].push({cb:s,status:e}),this.instance}listenerCount(t){return null!=this._events[t]?this._events[t].length:0}async trigger(t,...e){var s,r;try{if("debug"!==t&&this.trigger("debug",`Event triggered: ${t}`,e),null==this._events[t])return;return this._events[t]=this._events[t].filter((function(t){return"none"!==t.status})),r=this._events[t].map((async t=>{var s,r;if("none"!==t.status){"once"===t.status&&(t.status="none");try{return"function"==typeof(null!=(r="function"==typeof t.cb?t.cb(...e):void 0)?r.then:void 0)?await r:r}catch(t){return s=t,this.trigger("error",s),null}}})),(await Promise.all(r)).find((function(t){return null!=t}))}catch(t){return s=t,this.trigger("error",s),null}}};e=n,r=o;var h,a,u=class extends Error{};a=i,h=u;var l,c,d=class{constructor(t,e,s,r,i,n,o,h){this.task=t,this.args=e,this.rejectOnDrop=i,this.Events=n,this._states=o,this.Promise=h,this.options=a.load(s,r),this.options.priority=this._sanitizePriority(this.options.priority),this.options.id===r.id&&(this.options.id=`${this.options.id}-${this._randomIndex()}`),this.promise=new this.Promise(((t,e)=>{this._resolve=t,this._reject=e})),this.retryCount=0}_sanitizePriority(t){var e;return(e=~~t!==t?5:t)<0?0:e>9?9:e}_randomIndex(){return Math.random().toString(36).slice(2)}doDrop({error:t,message:e="This job has been dropped by Bottleneck"}={}){return!!this._states.remove(this.options.id)&&(this.rejectOnDrop&&this._reject(null!=t?t:new h(e)),this.Events.trigger("dropped",{args:this.args,options:this.options,task:this.task,promise:this.promise}),!0)}_assertStatus(t){var e;if((e=this._states.jobStatus(this.options.id))!==t&&("DONE"!==t||null!==e))throw new h(`Invalid job status ${e}, expected ${t}. Please open an issue at https://github.com/SGrondin/bottleneck/issues`)}doReceive(){return this._states.start(this.options.id),this.Events.trigger("received",{args:this.args,options:this.options})}doQueue(t,e){return this._assertStatus("RECEIVED"),this._states.next(this.options.id),this.Events.trigger("queued",{args:this.args,options:this.options,reachedHWM:t,blocked:e})}doRun(){return 0===this.retryCount?(this._assertStatus("QUEUED"),this._states.next(this.options.id)):this._assertStatus("EXECUTING"),this.Events.trigger("scheduled",{args:this.args,options:this.options})}async doExecute(t,e,s,r){var i,n,o;0===this.retryCount?(this._assertStatus("RUNNING"),this._states.next(this.options.id)):this._assertStatus("EXECUTING"),n={args:this.args,options:this.options,retryCount:this.retryCount},this.Events.trigger("executing",n);try{if(o=await(null!=t?t.schedule(this.options,this.task,...this.args):this.task(...this.args)),e())return this.doDone(n),await r(this.options,n),this._assertStatus("DONE"),this._resolve(o)}catch(t){return i=t,this._onFailure(i,n,e,s,r)}}doExpire(t,e,s){var r,i;return this._states.jobStatus("RUNNING"===this.options.id)&&this._states.next(this.options.id),this._assertStatus("EXECUTING"),i={args:this.args,options:this.options,retryCount:this.retryCount},r=new h(`This job timed out after ${this.options.expiration} ms.`),this._onFailure(r,i,t,e,s)}async _onFailure(t,e,s,r,i){var n,o;if(s())return null!=(n=await this.Events.trigger("failed",t,e))?(o=~~n,this.Events.trigger("retry",`Retrying ${this.options.id} after ${o} ms`,e),this.retryCount++,r(o)):(this.doDone(e),await i(this.options,e),this._assertStatus("DONE"),this._reject(t))}doDone(t){return this._assertStatus("EXECUTING"),this._states.next(this.options.id),this.Events.trigger("done",t)}};c=i,l=u;var p;p=u;var _;_=n;var v,f,m,g,y,b="2.19.5",w={version:b},E=Object.freeze({version:b,default:w}),O=()=>console.log("You must import the full version of Bottleneck in order to use this feature."),x=()=>console.log("You must import the full version of Bottleneck in order to use this feature.");y=i,v=o,m=O,f=x,g=()=>console.log("You must import the full version of Bottleneck in order to use this feature.");var j,k,I=function(){class t{constructor(t={}){this.deleteKey=this.deleteKey.bind(this),this.limiterOptions=t,y.load(this.limiterOptions,this.defaults,this),this.Events=new v(this),this.instances={},this.Bottleneck=M,this._startAutoCleanup(),this.sharedConnection=null!=this.connection,null==this.connection&&("redis"===this.limiterOptions.datastore?this.connection=new m(Object.assign({},this.limiterOptions,{Events:this.Events})):"ioredis"===this.limiterOptions.datastore&&(this.connection=new f(Object.assign({},this.limiterOptions,{Events:this.Events}))))}key(t=""){var e;return null!=(e=this.instances[t])?e:(()=>{var e;return e=this.instances[t]=new this.Bottleneck(Object.assign(this.limiterOptions,{id:`${this.id}-${t}`,timeout:this.timeout,connection:this.connection})),this.Events.trigger("created",e,t),e})()}async deleteKey(t=""){var e,s;return s=this.instances[t],this.connection&&(e=await this.connection.__runCommand__(["del",...g.allKeys(`${this.id}-${t}`)])),null!=s&&(delete this.instances[t],await s.disconnect()),null!=s||e>0}limiters(){var t,e,s,r;for(t in s=[],e=this.instances)r=e[t],s.push({key:t,limiter:r});return s}keys(){return Object.keys(this.instances)}async clusterKeys(){var t,e,s,r,i,n,o,h,a;if(null==this.connection)return this.Promise.resolve(this.keys());for(n=[],t=null,a=`b_${this.id}-`.length,e="_settings".length;0!==t;)for([h,s]=await this.connection.__runCommand__(["scan",null!=t?t:0,"match",`b_${this.id}-*_settings`,"count",1e4]),t=~~h,r=0,o=s.length;r<o;r++)i=s[r],n.push(i.slice(a,-e));return n}_startAutoCleanup(){var t;return clearInterval(this.interval),"function"==typeof(t=this.interval=setInterval((async()=>{var t,e,s,r,i,n;for(e in i=Date.now(),r=[],s=this.instances){n=s[e];try{await n._store.__groupCheck__(i)?r.push(this.deleteKey(e)):r.push(void 0)}catch(e){t=e,r.push(n.Events.trigger("error",t))}}return r}),this.timeout/2)).unref?t.unref():void 0}updateSettings(t={}){if(y.overwrite(t,this.defaults,this),y.overwrite(t,t,this.limiterOptions),null!=t.timeout)return this._startAutoCleanup()}disconnect(t=!0){var e;if(!this.sharedConnection)return null!=(e=this.connection)?e.disconnect(t):void 0}}return t.prototype.defaults={timeout:3e5,connection:null,Promise,id:"group-key"},t}.call(t);k=i,j=o;var R,D,C,T,P,S,L,A,q,$=function(){class t{constructor(t={}){this.options=t,k.load(this.options,this.defaults,this),this.Events=new j(this),this._arr=[],this._resetPromise(),this._lastFlush=Date.now()}_resetPromise(){return this._promise=new this.Promise(((t,e)=>this._resolve=t))}_flush(){return clearTimeout(this._timeout),this._lastFlush=Date.now(),this._resolve(),this.Events.trigger("batch",this._arr),this._arr=[],this._resetPromise()}add(t){var e;return this._arr.push(t),e=this._promise,this._arr.length===this.maxSize?this._flush():null!=this.maxTime&&1===this._arr.length&&(this._timeout=setTimeout((()=>this._flush()),this.maxTime)),e}}return t.prototype.defaults={maxTime:null,maxSize:null,Promise},t}.call(t),N=(q=E)&&q.default||q,B=[].splice;A=i,T=class{constructor(t){this.Events=new r(this),this._length=0,this._lists=function(){var s,r,i;for(i=[],s=1,r=t;1<=r?s<=r:s>=r;1<=r?++s:--s)i.push(new e((()=>this.incr()),(()=>this.decr())));return i}.call(this)}incr(){if(0==this._length++)return this.Events.trigger("leftzero")}decr(){if(0==--this._length)return this.Events.trigger("zero")}push(t){return this._lists[t.options.priority].push(t)}queued(t){return null!=t?this._lists[t].length:this._length}shiftAll(t){return this._lists.forEach((function(e){return e.forEachShift(t)}))}getFirst(t=this._lists){var e,s,r;for(e=0,s=t.length;e<s;e++)if((r=t[e]).length>0)return r;return[]}shiftLastFrom(t){return this.getFirst(this._lists.slice(t).reverse()).shift()}},D=d,C=class{constructor(t,e,s){this.instance=t,this.storeOptions=e,this.clientId=this.instance._randomIndex(),c.load(s,s,this),this._nextRequest=this._lastReservoirRefresh=this._lastReservoirIncrease=Date.now(),this._running=0,this._done=0,this._unblockTime=0,this.ready=this.Promise.resolve(),this.clients={},this._startHeartbeat()}_startHeartbeat(){var t;return null==this.heartbeat&&(null!=this.storeOptions.reservoirRefreshInterval&&null!=this.storeOptions.reservoirRefreshAmount||null!=this.storeOptions.reservoirIncreaseInterval&&null!=this.storeOptions.reservoirIncreaseAmount)?"function"==typeof(t=this.heartbeat=setInterval((()=>{var t,e,s,r,i;if(r=Date.now(),null!=this.storeOptions.reservoirRefreshInterval&&r>=this._lastReservoirRefresh+this.storeOptions.reservoirRefreshInterval&&(this._lastReservoirRefresh=r,this.storeOptions.reservoir=this.storeOptions.reservoirRefreshAmount,this.instance._drainAll(this.computeCapacity())),null!=this.storeOptions.reservoirIncreaseInterval&&r>=this._lastReservoirIncrease+this.storeOptions.reservoirIncreaseInterval&&(({reservoirIncreaseAmount:t,reservoirIncreaseMaximum:s,reservoir:i}=this.storeOptions),this._lastReservoirIncrease=r,(e=null!=s?Math.min(t,s-i):t)>0))return this.storeOptions.reservoir+=e,this.instance._drainAll(this.computeCapacity())}),this.heartbeatInterval)).unref?t.unref():void 0:clearInterval(this.heartbeat)}async __publish__(t){return await this.yieldLoop(),this.instance.Events.trigger("message",t.toString())}async __disconnect__(t){return await this.yieldLoop(),clearInterval(this.heartbeat),this.Promise.resolve()}yieldLoop(t=0){return new this.Promise((function(e,s){return setTimeout(e,t)}))}computePenalty(){var t;return null!=(t=this.storeOptions.penalty)?t:15*this.storeOptions.minTime||5e3}async __updateSettings__(t){return await this.yieldLoop(),c.overwrite(t,t,this.storeOptions),this._startHeartbeat(),this.instance._drainAll(this.computeCapacity()),!0}async __running__(){return await this.yieldLoop(),this._running}async __queued__(){return await this.yieldLoop(),this.instance.queued()}async __done__(){return await this.yieldLoop(),this._done}async __groupCheck__(t){return await this.yieldLoop(),this._nextRequest+this.timeout<t}computeCapacity(){var t,e;return({maxConcurrent:t,reservoir:e}=this.storeOptions),null!=t&&null!=e?Math.min(t-this._running,e):null!=t?t-this._running:null!=e?e:null}conditionsCheck(t){var e;return null==(e=this.computeCapacity())||t<=e}async __incrementReservoir__(t){var e;return await this.yieldLoop(),e=this.storeOptions.reservoir+=t,this.instance._drainAll(this.computeCapacity()),e}async __currentReservoir__(){return await this.yieldLoop(),this.storeOptions.reservoir}isBlocked(t){return this._unblockTime>=t}check(t,e){return this.conditionsCheck(t)&&this._nextRequest-e<=0}async __check__(t){var e;return await this.yieldLoop(),e=Date.now(),this.check(t,e)}async __register__(t,e,s){var r,i;return await this.yieldLoop(),r=Date.now(),this.conditionsCheck(e)?(this._running+=e,null!=this.storeOptions.reservoir&&(this.storeOptions.reservoir-=e),i=Math.max(this._nextRequest-r,0),this._nextRequest=r+i+this.storeOptions.minTime,{success:!0,wait:i,reservoir:this.storeOptions.reservoir}):{success:!1}}strategyIsBlock(){return 3===this.storeOptions.strategy}async __submit__(t,e){var s,r,i;if(await this.yieldLoop(),null!=this.storeOptions.maxConcurrent&&e>this.storeOptions.maxConcurrent)throw new l(`Impossible to add a job having a weight of ${e} to a limiter having a maxConcurrent setting of ${this.storeOptions.maxConcurrent}`);return r=Date.now(),i=null!=this.storeOptions.highWater&&t===this.storeOptions.highWater&&!this.check(e,r),(s=this.strategyIsBlock()&&(i||this.isBlocked(r)))&&(this._unblockTime=r+this.computePenalty(),this._nextRequest=this._unblockTime+this.storeOptions.minTime,this.instance._dropAllQueued()),{reachedHWM:i,blocked:s,strategy:this.storeOptions.strategy}}async __free__(t,e){return await this.yieldLoop(),this._running-=e,this._done+=e,this.instance._drainAll(this.computeCapacity()),{running:this._running}}},P=()=>console.log("You must import the full version of Bottleneck in order to use this feature."),R=o,S=class{constructor(t){this.status=t,this._jobs={},this.counts=this.status.map((function(){return 0}))}next(t){var e,s;return s=(e=this._jobs[t])+1,null!=e&&s<this.status.length?(this.counts[e]--,this.counts[s]++,this._jobs[t]++):null!=e?(this.counts[e]--,delete this._jobs[t]):void 0}start(t){return 0,this._jobs[t]=0,this.counts[0]++}remove(t){var e;return null!=(e=this._jobs[t])&&(this.counts[e]--,delete this._jobs[t]),null!=e}jobStatus(t){var e;return null!=(e=this.status[this._jobs[t]])?e:null}statusJobs(t){var e,s,r,i;if(null!=t){if((s=this.status.indexOf(t))<0)throw new p(`status must be one of ${this.status.join(", ")}`);for(e in i=[],r=this._jobs)r[e]===s&&i.push(e);return i}return Object.keys(this._jobs)}statusCounts(){return this.counts.reduce(((t,e,s)=>(t[this.status[s]]=e,t)),{})}},L=class{constructor(t,e){this.schedule=this.schedule.bind(this),this.name=t,this.Promise=e,this._running=0,this._queue=new _}isEmpty(){return 0===this._queue.length}async _tryToRun(){var t,e,s,r,i,n,o;if(this._running<1&&this._queue.length>0)return this._running++,({task:o,args:t,resolve:i,reject:r}=this._queue.shift()),e=await async function(){try{return n=await o(...t),function(){return i(n)}}catch(t){return s=t,function(){return r(s)}}}(),this._running--,this._tryToRun(),e()}schedule(t,...e){var s,r,i;return i=r=null,s=new this.Promise((function(t,e){return i=t,r=e})),this._queue.push({task:t,args:e,resolve:i,reject:r}),this._tryToRun(),s}};var M=function(){class t{constructor(e={},...s){var r,i;this._addToQueue=this._addToQueue.bind(this),this._validateOptions(e,s),A.load(e,this.instanceDefaults,this),this._queues=new T(10),this._scheduled={},this._states=new S(["RECEIVED","QUEUED","RUNNING","EXECUTING"].concat(this.trackDoneStatus?["DONE"]:[])),this._limiter=null,this.Events=new R(this),this._submitLock=new L("submit",this.Promise),this._registerLock=new L("register",this.Promise),i=A.load(e,this.storeDefaults,{}),this._store=function(){if("redis"===this.datastore||"ioredis"===this.datastore||null!=this.connection)return r=A.load(e,this.redisStoreDefaults,{}),new P(this,i,r);if("local"===this.datastore)return r=A.load(e,this.localStoreDefaults,{}),new C(this,i,r);throw new t.prototype.BottleneckError(`Invalid datastore type: ${this.datastore}`)}.call(this),this._queues.on("leftzero",(()=>{var t;return null!=(t=this._store.heartbeat)&&"function"==typeof t.ref?t.ref():void 0})),this._queues.on("zero",(()=>{var t;return null!=(t=this._store.heartbeat)&&"function"==typeof t.unref?t.unref():void 0}))}_validateOptions(e,s){if(null==e||"object"!=typeof e||0!==s.length)throw new t.prototype.BottleneckError("Bottleneck v2 takes a single object argument. Refer to https://github.com/SGrondin/bottleneck#upgrading-to-v2 if you're upgrading from Bottleneck v1.")}ready(){return this._store.ready}clients(){return this._store.clients}channel(){return`b_${this.id}`}channel_client(){return`b_${this.id}_${this._store.clientId}`}publish(t){return this._store.__publish__(t)}disconnect(t=!0){return this._store.__disconnect__(t)}chain(t){return this._limiter=t,this}queued(t){return this._queues.queued(t)}clusterQueued(){return this._store.__queued__()}empty(){return 0===this.queued()&&this._submitLock.isEmpty()}running(){return this._store.__running__()}done(){return this._store.__done__()}jobStatus(t){return this._states.jobStatus(t)}jobs(t){return this._states.statusJobs(t)}counts(){return this._states.statusCounts()}_randomIndex(){return Math.random().toString(36).slice(2)}check(t=1){return this._store.__check__(t)}_clearGlobalState(t){return null!=this._scheduled[t]&&(clearTimeout(this._scheduled[t].expiration),delete this._scheduled[t],!0)}async _free(t,e,s,r){var i,n;try{if(({running:n}=await this._store.__free__(t,s.weight)),this.Events.trigger("debug",`Freed ${s.id}`,r),0===n&&this.empty())return this.Events.trigger("idle")}catch(t){return i=t,this.Events.trigger("error",i)}}_run(t,e,s){var r,i,n;return e.doRun(),r=this._clearGlobalState.bind(this,t),n=this._run.bind(this,t,e),i=this._free.bind(this,t,e),this._scheduled[t]={timeout:setTimeout((()=>e.doExecute(this._limiter,r,n,i)),s),expiration:null!=e.options.expiration?setTimeout((function(){return e.doExpire(r,n,i)}),s+e.options.expiration):void 0,job:e}}_drainOne(t){return this._registerLock.schedule((()=>{var e,s,r,i,n;return 0===this.queued()?this.Promise.resolve(null):(n=this._queues.getFirst(),({options:i,args:e}=r=n.first()),null!=t&&i.weight>t?this.Promise.resolve(null):(this.Events.trigger("debug",`Draining ${i.id}`,{args:e,options:i}),s=this._randomIndex(),this._store.__register__(s,i.weight,i.expiration).then((({success:t,wait:o,reservoir:h})=>{var a;return this.Events.trigger("debug",`Drained ${i.id}`,{success:t,args:e,options:i}),t?(n.shift(),(a=this.empty())&&this.Events.trigger("empty"),0===h&&this.Events.trigger("depleted",a),this._run(s,r,o),this.Promise.resolve(i.weight)):this.Promise.resolve(null)}))))}))}_drainAll(t,e=0){return this._drainOne(t).then((s=>{var r;return null!=s?(r=null!=t?t-s:t,this._drainAll(r,e+s)):this.Promise.resolve(e)})).catch((t=>this.Events.trigger("error",t)))}_dropAllQueued(t){return this._queues.shiftAll((function(e){return e.doDrop({message:t})}))}stop(e={}){var s,r;return e=A.load(e,this.stopDefaults),r=t=>{var e;return e=()=>{var e;return(e=this._states.counts)[0]+e[1]+e[2]+e[3]===t},new this.Promise(((t,s)=>e()?t():this.on("done",(()=>{if(e())return this.removeAllListeners("done"),t()}))))},s=e.dropWaitingJobs?(this._run=function(t,s){return s.doDrop({message:e.dropErrorMessage})},this._drainOne=()=>this.Promise.resolve(null),this._registerLock.schedule((()=>this._submitLock.schedule((()=>{var t,s,i;for(t in s=this._scheduled)i=s[t],"RUNNING"===this.jobStatus(i.job.options.id)&&(clearTimeout(i.timeout),clearTimeout(i.expiration),i.job.doDrop({message:e.dropErrorMessage}));return this._dropAllQueued(e.dropErrorMessage),r(0)}))))):this.schedule({priority:9,weight:0},(()=>r(1))),this._receive=function(s){return s._reject(new t.prototype.BottleneckError(e.enqueueErrorMessage))},this.stop=()=>this.Promise.reject(new t.prototype.BottleneckError("stop() has already been called")),s}async _addToQueue(e){var s,r,i,n,o,h,a;({args:s,options:n}=e);try{({reachedHWM:o,blocked:r,strategy:a}=await this._store.__submit__(this.queued(),n.weight))}catch(t){return i=t,this.Events.trigger("debug",`Could not queue ${n.id}`,{args:s,options:n,error:i}),e.doDrop({error:i}),!1}return r?(e.doDrop(),!0):o&&(null!=(h=a===t.prototype.strategy.LEAK?this._queues.shiftLastFrom(n.priority):a===t.prototype.strategy.OVERFLOW_PRIORITY?this._queues.shiftLastFrom(n.priority+1):a===t.prototype.strategy.OVERFLOW?e:void 0)&&h.doDrop(),null==h||a===t.prototype.strategy.OVERFLOW)?(null==h&&e.doDrop(),o):(e.doQueue(o,r),this._queues.push(e),await this._drainAll(),o)}_receive(e){return null!=this._states.jobStatus(e.options.id)?(e._reject(new t.prototype.BottleneckError(`A job with the same id already exists (id=${e.options.id})`)),!1):(e.doReceive(),this._submitLock.schedule(this._addToQueue,e))}submit(...t){var e,s,r,i,n,o,h;return"function"==typeof t[0]?(n=t,[s,...t]=n,[e]=B.call(t,-1),i=A.load({},this.jobDefaults)):(o=t,[i,s,...t]=o,[e]=B.call(t,-1),i=A.load(i,this.jobDefaults)),h=(...t)=>new this.Promise((function(e,r){return s(...t,(function(...t){return(null!=t[0]?r:e)(t)}))})),(r=new D(h,t,i,this.jobDefaults,this.rejectOnDrop,this.Events,this._states,this.Promise)).promise.then((function(t){return"function"==typeof e?e(...t):void 0})).catch((function(t){return Array.isArray(t)?"function"==typeof e?e(...t):void 0:"function"==typeof e?e(t):void 0})),this._receive(r)}schedule(...t){var e,s,r;return"function"==typeof t[0]?([r,...t]=t,s={}):[s,r,...t]=t,e=new D(r,t,s,this.jobDefaults,this.rejectOnDrop,this.Events,this._states,this.Promise),this._receive(e),e.promise}wrap(t){var e,s;return e=this.schedule.bind(this),(s=function(...s){return e(t.bind(this),...s)}).withOptions=function(s,...r){return e(s,t,...r)},s}async updateSettings(t={}){return await this._store.__updateSettings__(A.overwrite(t,this.storeDefaults)),A.overwrite(t,this.instanceDefaults,this),this}currentReservoir(){return this._store.__currentReservoir__()}incrementReservoir(t=0){return this._store.__incrementReservoir__(t)}}return t.default=t,t.Events=R,t.version=t.prototype.version=N.version,t.strategy=t.prototype.strategy={LEAK:1,OVERFLOW:2,OVERFLOW_PRIORITY:4,BLOCK:3},t.BottleneckError=t.prototype.BottleneckError=u,t.Group=t.prototype.Group=I,t.RedisConnection=t.prototype.RedisConnection=O,t.IORedisConnection=t.prototype.IORedisConnection=x,t.Batcher=t.prototype.Batcher=$,t.prototype.jobDefaults={priority:5,weight:1,expiration:null,id:"<no-id>"},t.prototype.storeDefaults={maxConcurrent:null,minTime:0,highWater:null,strategy:t.prototype.strategy.LEAK,penalty:null,reservoir:null,reservoirRefreshInterval:null,reservoirRefreshAmount:null,reservoirIncreaseInterval:null,reservoirIncreaseAmount:null,reservoirIncreaseMaximum:null},t.prototype.localStoreDefaults={Promise,timeout:null,heartbeatInterval:250},t.prototype.redisStoreDefaults={Promise,timeout:null,heartbeatInterval:5e3,clientTimeout:1e4,Redis:null,clientOptions:{},clusterNodes:null,clearDatastore:!1,connection:null},t.prototype.instanceDefaults={datastore:"local",connection:null,id:"<no-id>",rejectOnDrop:!0,trackDoneStatus:!1,Promise},t.prototype.stopDefaults={enqueueErrorMessage:"This limiter has been stopped and cannot accept new jobs.",dropWaitingJobs:!0,dropErrorMessage:"This limiter has been stopped."},t}.call(t);return M}()},268:e=>{"use strict";e.exports=t}},s={};function r(t){var i=s[t];if(void 0!==i)return i.exports;var n=s[t]={exports:{}};return e[t].call(n.exports,n,n.exports,r),n.exports}r.d=(t,e)=>{for(var s in e)r.o(e,s)&&!r.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:e[s]})},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),r.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})};var i={};return(()=>{"use strict";r.r(i),r.d(i,{children:()=>c,content:()=>p,limiter:()=>o,meta:()=>d,normalizeNode:()=>u,normalizePath:()=>a});var t=r(268),e=r(209);const s="www.canada.ca",n={Jan:"01",Feb:"02",Mar:"03",Apr:"04",May:"05",Jun:"06",Jul:"07",Aug:"08",Sep:"09",Oct:"10",Nov:"11",Dec:"12"},o=new e({reservoir:150,reservoirRefreshAmount:150,reservoirRefreshInterval:5e3,maxConcurrent:10,trackDoneStatus:!0});function h(t,e){return"https://"+s+t+e+"?_="+Date.now()}function a(t){if((t=new URL(t,"https://"+s)).hostname!==s)throw new Error("Invalid hostname "+t.hostname);if(t.pathname.startsWith("/content/dam/"))return t.pathname;if(t.pathname.startsWith("/content/canadasite/")&&(t.pathname=t.pathname.substr(19)),/^\/(en|fr)(\/|$)/.test(t.pathname))return t.pathname.replace(/\.[^\.]+$/,"").replace(/\/$/,"");throw new Error("Invalid path "+t)}function u(t){if("string"==typeof t)return{path:a(t)};if("object"==typeof t&&"string"==typeof t.path)return t.path=a(t.path),t;throw new Error("Invalid node")}function l(t){if(!t.ok)throw new Error(t.statusText);if(!t.url.includes(s))throw new Error("Redirect");if(t.url.includes("/404.html"))throw new Error("Not Found");return t}function c(e){return e=u(e),o.schedule({expiration:6e4,priority:4},(e=>t(e)),h(e.path,".sitemap.xml")).then(l).then((t=>t.text())).then((t=>(e.children=t.match(/<url>.*?<\/url>/g).map((t=>{let e=t.match(/<loc>([^<]+)<\/loc>/),s=t.match(/<lastmod>([^<]+)<\/lastmod>/);return{path:a(e[1]),lastmod:s?Date.parse(s[1]):null}})),e.children.length&&e.children[0].path===e.path&&(e.lastmod=e.children.shift().lastmod),e)))}function d(e){return e=u(e),o.schedule({expiration:3e4},(e=>t(e)),h(e.path,"/jcr:content.json")).then(l).then((t=>t.json())).then((t=>(Object.keys(t).forEach((e=>{"true"===t[e]?t[e]=!0:"false"===t[e]?t[e]=!1:e.endsWith("@TypeHint")?delete t[e]:"string"==typeof t[e]&&(t[e]=function(t){if(/^\d{4}-\d{2}-\d{2}$/.test(t))return Date.parse(t);let e=/^\w{3} (\w{3}) (\d{2}) (\d{4}) ([\d:]{8}) GMT([\-+]\d{4})$/.exec(t);return e?Date.parse(e[3]+"-"+n[e[1]]+"-"+e[2]+"T"+e[4]+e[5]):t}(t[e]))})),e.meta=t,e)))}function p(e){let s=(e=u(e)).path.startsWith("/content/dam/")?"":".html";return o.schedule({expiration:3e4},(e=>t(e)),h(e.path,s)).then(l).then((t=>t.headers.get("Content-Type").includes("application/json")?t.json():t.text())).then((t=>(e.content=t,e)))}})(),i})()));
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("cross-fetch")):"function"==typeof define&&define.amd?define("ca",["cross-fetch"],e):"object"==typeof exports?exports.ca=e(require("cross-fetch")):t.ca=e(t.fetch)}("undefined"!=typeof self?self:this,(t=>(()=>{var e={209:function(t,e,s){t.exports=function(){"use strict";var t="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:void 0!==s.g?s.g:"undefined"!=typeof self?self:{};var e,r,i={load:function(t,e,s={}){var r,i,n;for(r in e)n=e[r],s[r]=null!=(i=t[r])?i:n;return s},overwrite:function(t,e,s={}){var r,i;for(r in t)i=t[r],void 0!==e[r]&&(s[r]=i);return s}},n=class{constructor(t,e){this.incr=t,this.decr=e,this._first=null,this._last=null,this.length=0}push(t){var e;this.length++,"function"==typeof this.incr&&this.incr(),e={value:t,prev:this._last,next:null},null!=this._last?(this._last.next=e,this._last=e):this._first=this._last=e}shift(){var t;if(null!=this._first)return this.length--,"function"==typeof this.decr&&this.decr(),t=this._first.value,null!=(this._first=this._first.next)?this._first.prev=null:this._last=null,t}first(){if(null!=this._first)return this._first.value}getArray(){var t,e,s;for(t=this._first,s=[];null!=t;)s.push((e=t,t=t.next,e.value));return s}forEachShift(t){var e;for(e=this.shift();null!=e;)t(e),e=this.shift()}debug(){var t,e,s,r,i;for(t=this._first,i=[];null!=t;)i.push((e=t,t=t.next,{value:e.value,prev:null!=(s=e.prev)?s.value:void 0,next:null!=(r=e.next)?r.value:void 0}));return i}},o=class{constructor(t){if(this.instance=t,this._events={},null!=this.instance.on||null!=this.instance.once||null!=this.instance.removeAllListeners)throw new Error("An Emitter already exists for this object");this.instance.on=(t,e)=>this._addListener(t,"many",e),this.instance.once=(t,e)=>this._addListener(t,"once",e),this.instance.removeAllListeners=(t=null)=>null!=t?delete this._events[t]:this._events={}}_addListener(t,e,s){var r;return null==(r=this._events)[t]&&(r[t]=[]),this._events[t].push({cb:s,status:e}),this.instance}listenerCount(t){return null!=this._events[t]?this._events[t].length:0}async trigger(t,...e){var s,r;try{if("debug"!==t&&this.trigger("debug",`Event triggered: ${t}`,e),null==this._events[t])return;return this._events[t]=this._events[t].filter((function(t){return"none"!==t.status})),r=this._events[t].map((async t=>{var s,r;if("none"!==t.status){"once"===t.status&&(t.status="none");try{return"function"==typeof(null!=(r="function"==typeof t.cb?t.cb(...e):void 0)?r.then:void 0)?await r:r}catch(t){return s=t,this.trigger("error",s),null}}})),(await Promise.all(r)).find((function(t){return null!=t}))}catch(t){return s=t,this.trigger("error",s),null}}};e=n,r=o;var a,h,u=class extends Error{};h=i,a=u;var l,c,p=class{constructor(t,e,s,r,i,n,o,a){this.task=t,this.args=e,this.rejectOnDrop=i,this.Events=n,this._states=o,this.Promise=a,this.options=h.load(s,r),this.options.priority=this._sanitizePriority(this.options.priority),this.options.id===r.id&&(this.options.id=`${this.options.id}-${this._randomIndex()}`),this.promise=new this.Promise(((t,e)=>{this._resolve=t,this._reject=e})),this.retryCount=0}_sanitizePriority(t){var e;return(e=~~t!==t?5:t)<0?0:e>9?9:e}_randomIndex(){return Math.random().toString(36).slice(2)}doDrop({error:t,message:e="This job has been dropped by Bottleneck"}={}){return!!this._states.remove(this.options.id)&&(this.rejectOnDrop&&this._reject(null!=t?t:new a(e)),this.Events.trigger("dropped",{args:this.args,options:this.options,task:this.task,promise:this.promise}),!0)}_assertStatus(t){var e;if((e=this._states.jobStatus(this.options.id))!==t&&("DONE"!==t||null!==e))throw new a(`Invalid job status ${e}, expected ${t}. Please open an issue at https://github.com/SGrondin/bottleneck/issues`)}doReceive(){return this._states.start(this.options.id),this.Events.trigger("received",{args:this.args,options:this.options})}doQueue(t,e){return this._assertStatus("RECEIVED"),this._states.next(this.options.id),this.Events.trigger("queued",{args:this.args,options:this.options,reachedHWM:t,blocked:e})}doRun(){return 0===this.retryCount?(this._assertStatus("QUEUED"),this._states.next(this.options.id)):this._assertStatus("EXECUTING"),this.Events.trigger("scheduled",{args:this.args,options:this.options})}async doExecute(t,e,s,r){var i,n,o;0===this.retryCount?(this._assertStatus("RUNNING"),this._states.next(this.options.id)):this._assertStatus("EXECUTING"),n={args:this.args,options:this.options,retryCount:this.retryCount},this.Events.trigger("executing",n);try{if(o=await(null!=t?t.schedule(this.options,this.task,...this.args):this.task(...this.args)),e())return this.doDone(n),await r(this.options,n),this._assertStatus("DONE"),this._resolve(o)}catch(t){return i=t,this._onFailure(i,n,e,s,r)}}doExpire(t,e,s){var r,i;return this._states.jobStatus("RUNNING"===this.options.id)&&this._states.next(this.options.id),this._assertStatus("EXECUTING"),i={args:this.args,options:this.options,retryCount:this.retryCount},r=new a(`This job timed out after ${this.options.expiration} ms.`),this._onFailure(r,i,t,e,s)}async _onFailure(t,e,s,r,i){var n,o;if(s())return null!=(n=await this.Events.trigger("failed",t,e))?(o=~~n,this.Events.trigger("retry",`Retrying ${this.options.id} after ${o} ms`,e),this.retryCount++,r(o)):(this.doDone(e),await i(this.options,e),this._assertStatus("DONE"),this._reject(t))}doDone(t){return this._assertStatus("EXECUTING"),this._states.next(this.options.id),this.Events.trigger("done",t)}};c=i,l=u;var d;d=u;var _;_=n;var f,v,m,g,y,w="2.19.5",b={version:w},E=Object.freeze({version:w,default:b}),O=()=>console.log("You must import the full version of Bottleneck in order to use this feature."),j=()=>console.log("You must import the full version of Bottleneck in order to use this feature.");y=i,f=o,m=O,v=j,g=()=>console.log("You must import the full version of Bottleneck in order to use this feature.");var x,k,R=function(){class t{constructor(t={}){this.deleteKey=this.deleteKey.bind(this),this.limiterOptions=t,y.load(this.limiterOptions,this.defaults,this),this.Events=new f(this),this.instances={},this.Bottleneck=U,this._startAutoCleanup(),this.sharedConnection=null!=this.connection,null==this.connection&&("redis"===this.limiterOptions.datastore?this.connection=new m(Object.assign({},this.limiterOptions,{Events:this.Events})):"ioredis"===this.limiterOptions.datastore&&(this.connection=new v(Object.assign({},this.limiterOptions,{Events:this.Events}))))}key(t=""){var e;return null!=(e=this.instances[t])?e:(()=>{var e;return e=this.instances[t]=new this.Bottleneck(Object.assign(this.limiterOptions,{id:`${this.id}-${t}`,timeout:this.timeout,connection:this.connection})),this.Events.trigger("created",e,t),e})()}async deleteKey(t=""){var e,s;return s=this.instances[t],this.connection&&(e=await this.connection.__runCommand__(["del",...g.allKeys(`${this.id}-${t}`)])),null!=s&&(delete this.instances[t],await s.disconnect()),null!=s||e>0}limiters(){var t,e,s,r;for(t in s=[],e=this.instances)r=e[t],s.push({key:t,limiter:r});return s}keys(){return Object.keys(this.instances)}async clusterKeys(){var t,e,s,r,i,n,o,a,h;if(null==this.connection)return this.Promise.resolve(this.keys());for(n=[],t=null,h=`b_${this.id}-`.length,e="_settings".length;0!==t;)for([a,s]=await this.connection.__runCommand__(["scan",null!=t?t:0,"match",`b_${this.id}-*_settings`,"count",1e4]),t=~~a,r=0,o=s.length;r<o;r++)i=s[r],n.push(i.slice(h,-e));return n}_startAutoCleanup(){var t;return clearInterval(this.interval),"function"==typeof(t=this.interval=setInterval((async()=>{var t,e,s,r,i,n;for(e in i=Date.now(),r=[],s=this.instances){n=s[e];try{await n._store.__groupCheck__(i)?r.push(this.deleteKey(e)):r.push(void 0)}catch(e){t=e,r.push(n.Events.trigger("error",t))}}return r}),this.timeout/2)).unref?t.unref():void 0}updateSettings(t={}){if(y.overwrite(t,this.defaults,this),y.overwrite(t,t,this.limiterOptions),null!=t.timeout)return this._startAutoCleanup()}disconnect(t=!0){var e;if(!this.sharedConnection)return null!=(e=this.connection)?e.disconnect(t):void 0}}return t.prototype.defaults={timeout:3e5,connection:null,Promise,id:"group-key"},t}.call(t);k=i,x=o;var D,I,C,P,T,A,L,S,q,$=function(){class t{constructor(t={}){this.options=t,k.load(this.options,this.defaults,this),this.Events=new x(this),this._arr=[],this._resetPromise(),this._lastFlush=Date.now()}_resetPromise(){return this._promise=new this.Promise(((t,e)=>this._resolve=t))}_flush(){return clearTimeout(this._timeout),this._lastFlush=Date.now(),this._resolve(),this.Events.trigger("batch",this._arr),this._arr=[],this._resetPromise()}add(t){var e;return this._arr.push(t),e=this._promise,this._arr.length===this.maxSize?this._flush():null!=this.maxTime&&1===this._arr.length&&(this._timeout=setTimeout((()=>this._flush()),this.maxTime)),e}}return t.prototype.defaults={maxTime:null,maxSize:null,Promise},t}.call(t),B=(q=E)&&q.default||q,N=[].splice;S=i,P=class{constructor(t){this.Events=new r(this),this._length=0,this._lists=function(){var s,r,i;for(i=[],s=1,r=t;1<=r?s<=r:s>=r;1<=r?++s:--s)i.push(new e((()=>this.incr()),(()=>this.decr())));return i}.call(this)}incr(){if(0==this._length++)return this.Events.trigger("leftzero")}decr(){if(0==--this._length)return this.Events.trigger("zero")}push(t){return this._lists[t.options.priority].push(t)}queued(t){return null!=t?this._lists[t].length:this._length}shiftAll(t){return this._lists.forEach((function(e){return e.forEachShift(t)}))}getFirst(t=this._lists){var e,s,r;for(e=0,s=t.length;e<s;e++)if((r=t[e]).length>0)return r;return[]}shiftLastFrom(t){return this.getFirst(this._lists.slice(t).reverse()).shift()}},I=p,C=class{constructor(t,e,s){this.instance=t,this.storeOptions=e,this.clientId=this.instance._randomIndex(),c.load(s,s,this),this._nextRequest=this._lastReservoirRefresh=this._lastReservoirIncrease=Date.now(),this._running=0,this._done=0,this._unblockTime=0,this.ready=this.Promise.resolve(),this.clients={},this._startHeartbeat()}_startHeartbeat(){var t;return null==this.heartbeat&&(null!=this.storeOptions.reservoirRefreshInterval&&null!=this.storeOptions.reservoirRefreshAmount||null!=this.storeOptions.reservoirIncreaseInterval&&null!=this.storeOptions.reservoirIncreaseAmount)?"function"==typeof(t=this.heartbeat=setInterval((()=>{var t,e,s,r,i;if(r=Date.now(),null!=this.storeOptions.reservoirRefreshInterval&&r>=this._lastReservoirRefresh+this.storeOptions.reservoirRefreshInterval&&(this._lastReservoirRefresh=r,this.storeOptions.reservoir=this.storeOptions.reservoirRefreshAmount,this.instance._drainAll(this.computeCapacity())),null!=this.storeOptions.reservoirIncreaseInterval&&r>=this._lastReservoirIncrease+this.storeOptions.reservoirIncreaseInterval&&(({reservoirIncreaseAmount:t,reservoirIncreaseMaximum:s,reservoir:i}=this.storeOptions),this._lastReservoirIncrease=r,(e=null!=s?Math.min(t,s-i):t)>0))return this.storeOptions.reservoir+=e,this.instance._drainAll(this.computeCapacity())}),this.heartbeatInterval)).unref?t.unref():void 0:clearInterval(this.heartbeat)}async __publish__(t){return await this.yieldLoop(),this.instance.Events.trigger("message",t.toString())}async __disconnect__(t){return await this.yieldLoop(),clearInterval(this.heartbeat),this.Promise.resolve()}yieldLoop(t=0){return new this.Promise((function(e,s){return setTimeout(e,t)}))}computePenalty(){var t;return null!=(t=this.storeOptions.penalty)?t:15*this.storeOptions.minTime||5e3}async __updateSettings__(t){return await this.yieldLoop(),c.overwrite(t,t,this.storeOptions),this._startHeartbeat(),this.instance._drainAll(this.computeCapacity()),!0}async __running__(){return await this.yieldLoop(),this._running}async __queued__(){return await this.yieldLoop(),this.instance.queued()}async __done__(){return await this.yieldLoop(),this._done}async __groupCheck__(t){return await this.yieldLoop(),this._nextRequest+this.timeout<t}computeCapacity(){var t,e;return({maxConcurrent:t,reservoir:e}=this.storeOptions),null!=t&&null!=e?Math.min(t-this._running,e):null!=t?t-this._running:null!=e?e:null}conditionsCheck(t){var e;return null==(e=this.computeCapacity())||t<=e}async __incrementReservoir__(t){var e;return await this.yieldLoop(),e=this.storeOptions.reservoir+=t,this.instance._drainAll(this.computeCapacity()),e}async __currentReservoir__(){return await this.yieldLoop(),this.storeOptions.reservoir}isBlocked(t){return this._unblockTime>=t}check(t,e){return this.conditionsCheck(t)&&this._nextRequest-e<=0}async __check__(t){var e;return await this.yieldLoop(),e=Date.now(),this.check(t,e)}async __register__(t,e,s){var r,i;return await this.yieldLoop(),r=Date.now(),this.conditionsCheck(e)?(this._running+=e,null!=this.storeOptions.reservoir&&(this.storeOptions.reservoir-=e),i=Math.max(this._nextRequest-r,0),this._nextRequest=r+i+this.storeOptions.minTime,{success:!0,wait:i,reservoir:this.storeOptions.reservoir}):{success:!1}}strategyIsBlock(){return 3===this.storeOptions.strategy}async __submit__(t,e){var s,r,i;if(await this.yieldLoop(),null!=this.storeOptions.maxConcurrent&&e>this.storeOptions.maxConcurrent)throw new l(`Impossible to add a job having a weight of ${e} to a limiter having a maxConcurrent setting of ${this.storeOptions.maxConcurrent}`);return r=Date.now(),i=null!=this.storeOptions.highWater&&t===this.storeOptions.highWater&&!this.check(e,r),(s=this.strategyIsBlock()&&(i||this.isBlocked(r)))&&(this._unblockTime=r+this.computePenalty(),this._nextRequest=this._unblockTime+this.storeOptions.minTime,this.instance._dropAllQueued()),{reachedHWM:i,blocked:s,strategy:this.storeOptions.strategy}}async __free__(t,e){return await this.yieldLoop(),this._running-=e,this._done+=e,this.instance._drainAll(this.computeCapacity()),{running:this._running}}},T=()=>console.log("You must import the full version of Bottleneck in order to use this feature."),D=o,A=class{constructor(t){this.status=t,this._jobs={},this.counts=this.status.map((function(){return 0}))}next(t){var e,s;return s=(e=this._jobs[t])+1,null!=e&&s<this.status.length?(this.counts[e]--,this.counts[s]++,this._jobs[t]++):null!=e?(this.counts[e]--,delete this._jobs[t]):void 0}start(t){return 0,this._jobs[t]=0,this.counts[0]++}remove(t){var e;return null!=(e=this._jobs[t])&&(this.counts[e]--,delete this._jobs[t]),null!=e}jobStatus(t){var e;return null!=(e=this.status[this._jobs[t]])?e:null}statusJobs(t){var e,s,r,i;if(null!=t){if((s=this.status.indexOf(t))<0)throw new d(`status must be one of ${this.status.join(", ")}`);for(e in i=[],r=this._jobs)r[e]===s&&i.push(e);return i}return Object.keys(this._jobs)}statusCounts(){return this.counts.reduce(((t,e,s)=>(t[this.status[s]]=e,t)),{})}},L=class{constructor(t,e){this.schedule=this.schedule.bind(this),this.name=t,this.Promise=e,this._running=0,this._queue=new _}isEmpty(){return 0===this._queue.length}async _tryToRun(){var t,e,s,r,i,n,o;if(this._running<1&&this._queue.length>0)return this._running++,({task:o,args:t,resolve:i,reject:r}=this._queue.shift()),e=await async function(){try{return n=await o(...t),function(){return i(n)}}catch(t){return s=t,function(){return r(s)}}}(),this._running--,this._tryToRun(),e()}schedule(t,...e){var s,r,i;return i=r=null,s=new this.Promise((function(t,e){return i=t,r=e})),this._queue.push({task:t,args:e,resolve:i,reject:r}),this._tryToRun(),s}};var U=function(){class t{constructor(e={},...s){var r,i;this._addToQueue=this._addToQueue.bind(this),this._validateOptions(e,s),S.load(e,this.instanceDefaults,this),this._queues=new P(10),this._scheduled={},this._states=new A(["RECEIVED","QUEUED","RUNNING","EXECUTING"].concat(this.trackDoneStatus?["DONE"]:[])),this._limiter=null,this.Events=new D(this),this._submitLock=new L("submit",this.Promise),this._registerLock=new L("register",this.Promise),i=S.load(e,this.storeDefaults,{}),this._store=function(){if("redis"===this.datastore||"ioredis"===this.datastore||null!=this.connection)return r=S.load(e,this.redisStoreDefaults,{}),new T(this,i,r);if("local"===this.datastore)return r=S.load(e,this.localStoreDefaults,{}),new C(this,i,r);throw new t.prototype.BottleneckError(`Invalid datastore type: ${this.datastore}`)}.call(this),this._queues.on("leftzero",(()=>{var t;return null!=(t=this._store.heartbeat)&&"function"==typeof t.ref?t.ref():void 0})),this._queues.on("zero",(()=>{var t;return null!=(t=this._store.heartbeat)&&"function"==typeof t.unref?t.unref():void 0}))}_validateOptions(e,s){if(null==e||"object"!=typeof e||0!==s.length)throw new t.prototype.BottleneckError("Bottleneck v2 takes a single object argument. Refer to https://github.com/SGrondin/bottleneck#upgrading-to-v2 if you're upgrading from Bottleneck v1.")}ready(){return this._store.ready}clients(){return this._store.clients}channel(){return`b_${this.id}`}channel_client(){return`b_${this.id}_${this._store.clientId}`}publish(t){return this._store.__publish__(t)}disconnect(t=!0){return this._store.__disconnect__(t)}chain(t){return this._limiter=t,this}queued(t){return this._queues.queued(t)}clusterQueued(){return this._store.__queued__()}empty(){return 0===this.queued()&&this._submitLock.isEmpty()}running(){return this._store.__running__()}done(){return this._store.__done__()}jobStatus(t){return this._states.jobStatus(t)}jobs(t){return this._states.statusJobs(t)}counts(){return this._states.statusCounts()}_randomIndex(){return Math.random().toString(36).slice(2)}check(t=1){return this._store.__check__(t)}_clearGlobalState(t){return null!=this._scheduled[t]&&(clearTimeout(this._scheduled[t].expiration),delete this._scheduled[t],!0)}async _free(t,e,s,r){var i,n;try{if(({running:n}=await this._store.__free__(t,s.weight)),this.Events.trigger("debug",`Freed ${s.id}`,r),0===n&&this.empty())return this.Events.trigger("idle")}catch(t){return i=t,this.Events.trigger("error",i)}}_run(t,e,s){var r,i,n;return e.doRun(),r=this._clearGlobalState.bind(this,t),n=this._run.bind(this,t,e),i=this._free.bind(this,t,e),this._scheduled[t]={timeout:setTimeout((()=>e.doExecute(this._limiter,r,n,i)),s),expiration:null!=e.options.expiration?setTimeout((function(){return e.doExpire(r,n,i)}),s+e.options.expiration):void 0,job:e}}_drainOne(t){return this._registerLock.schedule((()=>{var e,s,r,i,n;return 0===this.queued()?this.Promise.resolve(null):(n=this._queues.getFirst(),({options:i,args:e}=r=n.first()),null!=t&&i.weight>t?this.Promise.resolve(null):(this.Events.trigger("debug",`Draining ${i.id}`,{args:e,options:i}),s=this._randomIndex(),this._store.__register__(s,i.weight,i.expiration).then((({success:t,wait:o,reservoir:a})=>{var h;return this.Events.trigger("debug",`Drained ${i.id}`,{success:t,args:e,options:i}),t?(n.shift(),(h=this.empty())&&this.Events.trigger("empty"),0===a&&this.Events.trigger("depleted",h),this._run(s,r,o),this.Promise.resolve(i.weight)):this.Promise.resolve(null)}))))}))}_drainAll(t,e=0){return this._drainOne(t).then((s=>{var r;return null!=s?(r=null!=t?t-s:t,this._drainAll(r,e+s)):this.Promise.resolve(e)})).catch((t=>this.Events.trigger("error",t)))}_dropAllQueued(t){return this._queues.shiftAll((function(e){return e.doDrop({message:t})}))}stop(e={}){var s,r;return e=S.load(e,this.stopDefaults),r=t=>{var e;return e=()=>{var e;return(e=this._states.counts)[0]+e[1]+e[2]+e[3]===t},new this.Promise(((t,s)=>e()?t():this.on("done",(()=>{if(e())return this.removeAllListeners("done"),t()}))))},s=e.dropWaitingJobs?(this._run=function(t,s){return s.doDrop({message:e.dropErrorMessage})},this._drainOne=()=>this.Promise.resolve(null),this._registerLock.schedule((()=>this._submitLock.schedule((()=>{var t,s,i;for(t in s=this._scheduled)i=s[t],"RUNNING"===this.jobStatus(i.job.options.id)&&(clearTimeout(i.timeout),clearTimeout(i.expiration),i.job.doDrop({message:e.dropErrorMessage}));return this._dropAllQueued(e.dropErrorMessage),r(0)}))))):this.schedule({priority:9,weight:0},(()=>r(1))),this._receive=function(s){return s._reject(new t.prototype.BottleneckError(e.enqueueErrorMessage))},this.stop=()=>this.Promise.reject(new t.prototype.BottleneckError("stop() has already been called")),s}async _addToQueue(e){var s,r,i,n,o,a,h;({args:s,options:n}=e);try{({reachedHWM:o,blocked:r,strategy:h}=await this._store.__submit__(this.queued(),n.weight))}catch(t){return i=t,this.Events.trigger("debug",`Could not queue ${n.id}`,{args:s,options:n,error:i}),e.doDrop({error:i}),!1}return r?(e.doDrop(),!0):o&&(null!=(a=h===t.prototype.strategy.LEAK?this._queues.shiftLastFrom(n.priority):h===t.prototype.strategy.OVERFLOW_PRIORITY?this._queues.shiftLastFrom(n.priority+1):h===t.prototype.strategy.OVERFLOW?e:void 0)&&a.doDrop(),null==a||h===t.prototype.strategy.OVERFLOW)?(null==a&&e.doDrop(),o):(e.doQueue(o,r),this._queues.push(e),await this._drainAll(),o)}_receive(e){return null!=this._states.jobStatus(e.options.id)?(e._reject(new t.prototype.BottleneckError(`A job with the same id already exists (id=${e.options.id})`)),!1):(e.doReceive(),this._submitLock.schedule(this._addToQueue,e))}submit(...t){var e,s,r,i,n,o,a;return"function"==typeof t[0]?(n=t,[s,...t]=n,[e]=N.call(t,-1),i=S.load({},this.jobDefaults)):(o=t,[i,s,...t]=o,[e]=N.call(t,-1),i=S.load(i,this.jobDefaults)),a=(...t)=>new this.Promise((function(e,r){return s(...t,(function(...t){return(null!=t[0]?r:e)(t)}))})),(r=new I(a,t,i,this.jobDefaults,this.rejectOnDrop,this.Events,this._states,this.Promise)).promise.then((function(t){return"function"==typeof e?e(...t):void 0})).catch((function(t){return Array.isArray(t)?"function"==typeof e?e(...t):void 0:"function"==typeof e?e(t):void 0})),this._receive(r)}schedule(...t){var e,s,r;return"function"==typeof t[0]?([r,...t]=t,s={}):[s,r,...t]=t,e=new I(r,t,s,this.jobDefaults,this.rejectOnDrop,this.Events,this._states,this.Promise),this._receive(e),e.promise}wrap(t){var e,s;return e=this.schedule.bind(this),(s=function(...s){return e(t.bind(this),...s)}).withOptions=function(s,...r){return e(s,t,...r)},s}async updateSettings(t={}){return await this._store.__updateSettings__(S.overwrite(t,this.storeDefaults)),S.overwrite(t,this.instanceDefaults,this),this}currentReservoir(){return this._store.__currentReservoir__()}incrementReservoir(t=0){return this._store.__incrementReservoir__(t)}}return t.default=t,t.Events=D,t.version=t.prototype.version=B.version,t.strategy=t.prototype.strategy={LEAK:1,OVERFLOW:2,OVERFLOW_PRIORITY:4,BLOCK:3},t.BottleneckError=t.prototype.BottleneckError=u,t.Group=t.prototype.Group=R,t.RedisConnection=t.prototype.RedisConnection=O,t.IORedisConnection=t.prototype.IORedisConnection=j,t.Batcher=t.prototype.Batcher=$,t.prototype.jobDefaults={priority:5,weight:1,expiration:null,id:"<no-id>"},t.prototype.storeDefaults={maxConcurrent:null,minTime:0,highWater:null,strategy:t.prototype.strategy.LEAK,penalty:null,reservoir:null,reservoirRefreshInterval:null,reservoirRefreshAmount:null,reservoirIncreaseInterval:null,reservoirIncreaseAmount:null,reservoirIncreaseMaximum:null},t.prototype.localStoreDefaults={Promise,timeout:null,heartbeatInterval:250},t.prototype.redisStoreDefaults={Promise,timeout:null,heartbeatInterval:5e3,clientTimeout:1e4,Redis:null,clientOptions:{},clusterNodes:null,clearDatastore:!1,connection:null},t.prototype.instanceDefaults={datastore:"local",connection:null,id:"<no-id>",rejectOnDrop:!0,trackDoneStatus:!1,Promise},t.prototype.stopDefaults={enqueueErrorMessage:"This limiter has been stopped and cannot accept new jobs.",dropWaitingJobs:!0,dropErrorMessage:"This limiter has been stopped."},t}.call(t);return U}()},310:t=>{"use strict";t.exports=t=>{if("[object Object]"!==Object.prototype.toString.call(t))return!1;const e=Object.getPrototypeOf(t);return null===e||e===Object.prototype}},942:function(t,e,s){"use strict";const r=s(310),{hasOwnProperty:i}=Object.prototype,{propertyIsEnumerable:n}=Object,o=(t,e,s)=>Object.defineProperty(t,e,{value:s,writable:!0,enumerable:!0,configurable:!0}),a=this,h={concatArrays:!1,ignoreUndefined:!1},u=t=>{const e=[];for(const s in t)i.call(t,s)&&e.push(s);if(Object.getOwnPropertySymbols){const s=Object.getOwnPropertySymbols(t);for(const r of s)n.call(t,r)&&e.push(r)}return e};function l(t){return Array.isArray(t)?function(t){const e=t.slice(0,0);return u(t).forEach((s=>{o(e,s,l(t[s]))})),e}(t):r(t)?function(t){const e=null===Object.getPrototypeOf(t)?Object.create(null):{};return u(t).forEach((s=>{o(e,s,l(t[s]))})),e}(t):t}const c=(t,e,s,r)=>(s.forEach((s=>{void 0===e[s]&&r.ignoreUndefined||(s in t&&t[s]!==Object.getPrototypeOf(t)?o(t,s,p(t[s],e[s],r)):o(t,s,l(e[s])))})),t);function p(t,e,s){return s.concatArrays&&Array.isArray(t)&&Array.isArray(e)?((t,e,s)=>{let r=t.slice(0,0),n=0;return[t,e].forEach((e=>{const a=[];for(let s=0;s<e.length;s++)i.call(e,s)&&(a.push(String(s)),o(r,n++,e===t?e[s]:l(e[s])));r=c(r,e,u(e).filter((t=>!a.includes(t))),s)})),r})(t,e,s):r(e)&&r(t)?c(t,e,u(e),s):l(e)}t.exports=function(...t){const e=p(l(h),this!==a&&this||{},h);let s={_:{}};for(const i of t)if(void 0!==i){if(!r(i))throw new TypeError("`"+i+"` is not an Option Object");s=p(s,{_:i},e)}return s._}},322:(t,e,s)=>{const r=s(427),i=s(942),n=s(526),o={jobOptions:{priority:0}};t.exports=async(t,e)=>{t=r(t,"children"),e=i(o,e);let s=await n(t,e),a=(await s.text()).match(/<url>.*?<\/url>/g).map((t=>{let e=t.match(/<loc>([^<]+)<\/loc>/),s=t.match(/<lastmod>([^<]+)<\/lastmod>/);return{path:r(e[1]),lastmod:s?Date.parse(s[1]):null}}));return a.length&&a[0].path===r(t)&&a.shift(),a}},565:(t,e,s)=>{const r=s(427),i=s(942),n=s(526),o={jobOptions:{priority:0}};t.exports=async(t,e)=>{t=r(t,"content"),e=i(o,e);let s=await n(t,e),a=s.headers.get("Content-Type");if(a.includes("/json"))return s.json();let h=await s.text();return a.includes("text/html")&&(h=h.replace(/\s+/g," ")),h}},526:(t,e,s)=>{const r=s(268),i=new(s(209))({reservoir:150,reservoirRefreshAmount:150,reservoirRefreshInterval:5e3,maxConcurrent:10,trackDoneStatus:!0}),n=(t,e)=>r(t,e);t.exports=e=async(t,e)=>{let s=await i.schedule(e?.jobOptions||{},n,t,e);if(!s.ok)throw new Error(s.statusText);if("string"==typeof t&&(t=new URL(t)),t.hostname!==new URL(s.url).hostname)throw new Error("Redirect to invalid host");return s},e.limiter=i},138:(t,e,s)=>{const r=s(526),i=s(427),n=s(322),o=s(565),a=s(873);t.exports={fetch:r,normalize:i,children:n,content:o,meta:a}},873:(t,e,s)=>{const r=s(427),i=s(942),n=s(526),o={jobOptions:{expiration:3e4}},a={Jan:"01",Feb:"02",Mar:"03",Apr:"04",May:"05",Jun:"06",Jul:"07",Aug:"08",Sep:"09",Oct:"10",Nov:"11",Dec:"12"};t.exports=async(t,e)=>{t=r(t,"meta"),e=i(o,e);let s=await n(t,e);if(!s.headers.get("content-type").includes("/json"))throw new Error("Unexpected response content-type");let h=await s.json();return Object.keys(h).forEach((t=>{"true"===h[t]?h[t]=!0:"false"===h[t]?h[t]=!1:t.endsWith("@TypeHint")?delete h[t]:"string"==typeof h[t]?h[t]=function(t){if(/^\d{4}-\d{2}-\d{2}$/.test(t))return Date.parse(t);let e=/^\w{3} (\w{3}) (\d{2}) (\d{4}) ([\d:]{8}) GMT([\-+]\d{4})$/.exec(t);return e?Date.parse(e[3]+"-"+a[e[1]]+"-"+e[2]+"T"+e[4]+e[5]):t}(h[t]):Array.isArray(h[t])&&0===h[t].length&&delete h[t]})),Object.keys(h).sort().reduce(((t,e)=>(t[e]=h[e],t)),{})}},427:(t,e)=>{const s="https://www.canada.ca/";t.exports=(t,e="path")=>{if("string"==typeof t)t=new URL(t,s);else if(!(t instanceof URL))throw new TypeError("string or URL object expected");if(t.protocol="https",!t.href.startsWith(s))throw new Error("URL must start with "+s);let r=t.pathname.startsWith("/content/dam");if(r||(t.pathname=t.pathname.replace(/^\/content\/canadasite\//,"")),!/^\/(en|fr|content\/dam)(\/|\.|$)/.test(t.pathname))throw new Error("Invalid root");return t.pathname=t.pathname.replace(/\/+$/,""),"path"!==e&&t.searchParams.set("_",Date.now()),t.pathname.includes("/_jcr_content/par")?function(t,e){if("path"===e)throw new Error("Cant determine path of a reference node directly");if("meta"===e)t.pathname=t.pathname.replace(/\/image\.img\.\w+\//,"/image.json/");else if("children"===e)throw new Error("Cant load children of a reference node");return t.href}(t,e):r?function(t,e){let s=/(\/|\/\w+)$/.test(t.pathname);if("meta"===e)t.pathname+=s?".json":"/_jcr_content.json";else{if("children"===e)throw new Error("Cant load children of an asset node");if("content"===e&&s)throw new Error("Cant load content of an asset node")}return"path"===e?t.pathname:t.href}(t,e):function(t,e){return t.pathname=t.pathname.replace(/\/?\.(sitemap\.xml|[^/.]+)$/,""),"meta"===e?t.pathname+="/_jcr_content.json":"children"===e?t.pathname+=".sitemap.xml":"content"===e&&(t.pathname+=".html"),"path"===e?t.pathname:t.href}(t,e)}},268:e=>{"use strict";e.exports=t}},s={};function r(t){var i=s[t];if(void 0!==i)return i.exports;var n=s[t]={exports:{}};return e[t].call(n.exports,n,n.exports,r),n.exports}return r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),r(138)})()));
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "canada-api",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"description": "Cross platform API to fetch data from canada.ca",
|
|
5
5
|
"browser": "dist/ca.js",
|
|
6
|
-
"main": "src/index.
|
|
6
|
+
"main": "src/index.js",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
9
9
|
"build": "webpack",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"repository": {
|
|
13
13
|
"type": "git",
|
|
14
|
-
"url": "https://github.com/
|
|
14
|
+
"url": "https://github.com/dnd-mdn/canada-api.git"
|
|
15
15
|
},
|
|
16
16
|
"keywords": [
|
|
17
17
|
"canada",
|
|
@@ -20,17 +20,18 @@
|
|
|
20
20
|
],
|
|
21
21
|
"author": "Ben Soicher",
|
|
22
22
|
"license": "MIT",
|
|
23
|
-
"homepage": "https://github.com/
|
|
24
|
-
"bugs": "https://github.com/
|
|
23
|
+
"homepage": "https://github.com/dnd-mdn/canada-api#readme",
|
|
24
|
+
"bugs": "https://github.com/dnd-mdn/canada-api/issues",
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"webpack": "^5.72.0",
|
|
27
27
|
"webpack-cli": "^4.9.2"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"bottleneck": "^2.19.5",
|
|
31
|
-
"cross-fetch": "^3.1.5"
|
|
31
|
+
"cross-fetch": "^3.1.5",
|
|
32
|
+
"merge-options": "^3.0.4"
|
|
32
33
|
},
|
|
33
34
|
"engines": {
|
|
34
35
|
"node": ">= 10.0.0"
|
|
35
36
|
}
|
|
36
|
-
}
|
|
37
|
+
}
|
package/src/children.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const normalize = require('./normalize.js')
|
|
2
|
+
const merge = require('merge-options')
|
|
3
|
+
const fetch = require('./fetch.js')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Default fetch options
|
|
7
|
+
* @const {object}
|
|
8
|
+
*/
|
|
9
|
+
const defaultOptions = {
|
|
10
|
+
jobOptions: {
|
|
11
|
+
priority: 0
|
|
12
|
+
},
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get list of child nodes from sitemap
|
|
17
|
+
* @param {string} url node url
|
|
18
|
+
* @param {Object} [options] fetch options
|
|
19
|
+
* @returns {Promise<Array>}
|
|
20
|
+
*/
|
|
21
|
+
const children = async (url, options) => {
|
|
22
|
+
url = normalize(url, 'children')
|
|
23
|
+
options = merge(defaultOptions, options)
|
|
24
|
+
|
|
25
|
+
let response = await fetch(url, options)
|
|
26
|
+
let xml = await response.text()
|
|
27
|
+
|
|
28
|
+
// Parse XML sitemap
|
|
29
|
+
let children = xml.match(/<url>.*?<\/url>/g).map(url => {
|
|
30
|
+
let loc = url.match(/<loc>([^<]+)<\/loc>/)
|
|
31
|
+
let mod = url.match(/<lastmod>([^<]+)<\/lastmod>/)
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
path: normalize(loc[1]),
|
|
35
|
+
lastmod: mod ? Date.parse(mod[1]) : null
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// First entry may be the parent
|
|
40
|
+
if (children.length && children[0].path === normalize(url)) {
|
|
41
|
+
children.shift()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return children
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = exports = children
|
package/src/content.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const normalize = require('./normalize.js')
|
|
2
|
+
const merge = require('merge-options')
|
|
3
|
+
const fetch = require('./fetch.js')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Default fetch options
|
|
7
|
+
* @const {object}
|
|
8
|
+
*/
|
|
9
|
+
const defaultOptions = {
|
|
10
|
+
jobOptions: {
|
|
11
|
+
priority: 0
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get node content
|
|
17
|
+
* @param {string} url node url
|
|
18
|
+
* @param {Object} [options] fetch options
|
|
19
|
+
* @returns {Promise<any>}
|
|
20
|
+
*/
|
|
21
|
+
const content = async (url, options) => {
|
|
22
|
+
url = normalize(url, 'content')
|
|
23
|
+
options = merge(defaultOptions, options)
|
|
24
|
+
|
|
25
|
+
let response = await fetch(url, options)
|
|
26
|
+
let type = response.headers.get('Content-Type')
|
|
27
|
+
|
|
28
|
+
if (type.includes('/json')) {
|
|
29
|
+
return response.json()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let text = await response.text()
|
|
33
|
+
|
|
34
|
+
// Compress whitespace in html
|
|
35
|
+
if (type.includes('text/html')) {
|
|
36
|
+
text = text.replace(/\s+/g, ' ')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return text
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = exports = content
|
package/src/fetch.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const crossFetch = require('cross-fetch')
|
|
2
|
+
const Bottleneck = require('bottleneck/light.js')
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Default limiter options
|
|
6
|
+
* @see https://stackleap.io/js/bottleneck#user-content-constructor
|
|
7
|
+
* @const {object}
|
|
8
|
+
*/
|
|
9
|
+
const limiterOptions = {
|
|
10
|
+
reservoir: 150,
|
|
11
|
+
reservoirRefreshAmount: 150,
|
|
12
|
+
reservoirRefreshInterval: 5000,
|
|
13
|
+
maxConcurrent: 10,
|
|
14
|
+
trackDoneStatus: true
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Rate limiter
|
|
19
|
+
* @type {Bottleneck}
|
|
20
|
+
*/
|
|
21
|
+
const limiter = new Bottleneck(limiterOptions)
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Passthrough arguments to prevent running method on non window object
|
|
25
|
+
* @const {function}
|
|
26
|
+
*/
|
|
27
|
+
const passthrough = (url, options) => crossFetch(url, options)
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Modified rate limited fetch
|
|
31
|
+
* @param {string|URL} url
|
|
32
|
+
* @param {object} [options] Fetch options
|
|
33
|
+
* @returns {Promise<Response>}
|
|
34
|
+
*/
|
|
35
|
+
const fetch = async (url, options) => {
|
|
36
|
+
|
|
37
|
+
let response = await limiter.schedule(options?.jobOptions || {}, passthrough, url, options)
|
|
38
|
+
|
|
39
|
+
// Verify response
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
throw new Error(response.statusText)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (typeof url === 'string') {
|
|
45
|
+
url = new URL(url)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Prevent host redirect
|
|
49
|
+
if (url.hostname !== new URL(response.url).hostname) {
|
|
50
|
+
throw new Error('Redirect to invalid host')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return response
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Default export
|
|
57
|
+
module.exports = exports = fetch
|
|
58
|
+
|
|
59
|
+
// Expose the limiter
|
|
60
|
+
exports.limiter = limiter
|
package/src/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const fetch = require('./fetch.js')
|
|
2
|
+
const normalize = require('./normalize.js')
|
|
3
|
+
const children = require('./children.js')
|
|
4
|
+
const content = require('./content.js')
|
|
5
|
+
const meta = require ('./meta.js')
|
|
6
|
+
|
|
7
|
+
module.exports = exports = {
|
|
8
|
+
// Core
|
|
9
|
+
fetch,
|
|
10
|
+
normalize,
|
|
11
|
+
|
|
12
|
+
// Basic API
|
|
13
|
+
children,
|
|
14
|
+
content,
|
|
15
|
+
meta,
|
|
16
|
+
}
|
package/src/meta.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const normalize = require('./normalize.js')
|
|
2
|
+
const merge = require('merge-options')
|
|
3
|
+
const fetch = require('./fetch.js')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Default fetch options
|
|
7
|
+
* @const {object}
|
|
8
|
+
*/
|
|
9
|
+
const defaultOptions = {
|
|
10
|
+
jobOptions: {
|
|
11
|
+
expiration: 30000
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get node metadata from jcr content
|
|
17
|
+
* @param {string} url node path
|
|
18
|
+
* @param {Object} [options] fetch options
|
|
19
|
+
* @returns {Promise<Object>}
|
|
20
|
+
*/
|
|
21
|
+
const meta = async (url, options) => {
|
|
22
|
+
url = normalize(url, 'meta')
|
|
23
|
+
options = merge(defaultOptions, options)
|
|
24
|
+
|
|
25
|
+
let response = await fetch(url, options)
|
|
26
|
+
|
|
27
|
+
if (!response.headers.get('content-type').includes('/json')) {
|
|
28
|
+
throw new Error('Unexpected response content-type')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let json = await response.json()
|
|
32
|
+
|
|
33
|
+
// Format meta properties
|
|
34
|
+
Object.keys(json).forEach(key => {
|
|
35
|
+
if (json[key] === 'true') {
|
|
36
|
+
json[key] = true
|
|
37
|
+
} else if (json[key] === 'false') {
|
|
38
|
+
json[key] = false
|
|
39
|
+
} else if (key.endsWith('@TypeHint')) {
|
|
40
|
+
delete json[key]
|
|
41
|
+
} else if (typeof json[key] === 'string') {
|
|
42
|
+
json[key] = maybeParseDate(json[key])
|
|
43
|
+
} else if (Array.isArray(json[key]) && json[key].length === 0) {
|
|
44
|
+
delete json[key]
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// Sort object keys alphabetically for readability
|
|
49
|
+
return Object.keys(json).sort().reduce((obj, key) => {
|
|
50
|
+
obj[key] = json[key]
|
|
51
|
+
return obj
|
|
52
|
+
}, {})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Map month name to number
|
|
57
|
+
* @const {object}
|
|
58
|
+
*/
|
|
59
|
+
const months = {
|
|
60
|
+
'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06',
|
|
61
|
+
'Jul': '07', 'Aug': '08', 'Sep': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Try to parse a date
|
|
66
|
+
* @param {string} date
|
|
67
|
+
* @returns {number|string}
|
|
68
|
+
*/
|
|
69
|
+
function maybeParseDate(date) {
|
|
70
|
+
// Simple
|
|
71
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
|
72
|
+
return Date.parse(date)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// RFC1123
|
|
76
|
+
let m = /^\w{3} (\w{3}) (\d{2}) (\d{4}) ([\d:]{8}) GMT([\-+]\d{4})$/.exec(date)
|
|
77
|
+
if (m) {
|
|
78
|
+
return Date.parse(m[3] + '-' + months[m[1]] + '-' + m[2] + 'T' + m[4] + m[5])
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return date
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = exports = meta
|
package/src/normalize.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* Base URL
|
|
4
|
+
* @const {string}
|
|
5
|
+
*/
|
|
6
|
+
const baseURL = 'https://www.canada.ca/'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Normalize AEM URL
|
|
10
|
+
* @param {string|URL} url
|
|
11
|
+
* @param {string} [type=path]
|
|
12
|
+
* @returns {string}
|
|
13
|
+
*/
|
|
14
|
+
const normalize = (url, type = 'path') => {
|
|
15
|
+
|
|
16
|
+
if (typeof url === 'string') {
|
|
17
|
+
url = new URL(url, baseURL)
|
|
18
|
+
} else if (!(url instanceof URL)) {
|
|
19
|
+
throw new TypeError('string or URL object expected')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Force https
|
|
23
|
+
url.protocol = 'https'
|
|
24
|
+
|
|
25
|
+
// Verify domain
|
|
26
|
+
if (!url.href.startsWith(baseURL)) {
|
|
27
|
+
throw new Error('URL must start with ' + baseURL)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let isAsset = url.pathname.startsWith('/content/dam')
|
|
31
|
+
|
|
32
|
+
// Strip canadasite prefix
|
|
33
|
+
if (!isAsset) {
|
|
34
|
+
url.pathname = url.pathname.replace(/^\/content\/canadasite\//, '')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Verify root
|
|
38
|
+
if (!/^\/(en|fr|content\/dam)(\/|\.|$)/.test(url.pathname)) {
|
|
39
|
+
throw new Error('Invalid root')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Strip Trailing slashes
|
|
43
|
+
url.pathname = url.pathname.replace(/\/+$/, '')
|
|
44
|
+
|
|
45
|
+
// Cache busting
|
|
46
|
+
if (type !== 'path') {
|
|
47
|
+
url.searchParams.set('_', Date.now())
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Handle other URL types
|
|
51
|
+
if (url.pathname.includes('/_jcr_content/par')) {
|
|
52
|
+
return normalizeReference(url, type)
|
|
53
|
+
} else if (isAsset) {
|
|
54
|
+
return normalizeAsset(url, type)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return normalizePage(url, type)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Normalize AEM page
|
|
62
|
+
* @param {URL} url
|
|
63
|
+
* @param {string} type
|
|
64
|
+
* @returns {URL}
|
|
65
|
+
*/
|
|
66
|
+
function normalizePage(url, type) {
|
|
67
|
+
// Remove existing extension
|
|
68
|
+
url.pathname = url.pathname.replace(/\/?\.(sitemap\.xml|[^/.]+)$/, '')
|
|
69
|
+
|
|
70
|
+
if (type === 'meta') {
|
|
71
|
+
url.pathname += '/_jcr_content.json'
|
|
72
|
+
} else if (type === 'children') {
|
|
73
|
+
url.pathname += '.sitemap.xml'
|
|
74
|
+
} else if (type === 'content') {
|
|
75
|
+
url.pathname += '.html'
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return type === 'path' ? url.pathname : url.href
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Normalize AEM asset
|
|
83
|
+
* @param {URL} url
|
|
84
|
+
* @param {string} type
|
|
85
|
+
* @returns {URL}
|
|
86
|
+
*/
|
|
87
|
+
function normalizeAsset(url, type) {
|
|
88
|
+
let isDir = /(\/|\/\w+)$/.test(url.pathname)
|
|
89
|
+
|
|
90
|
+
if (type === 'meta') {
|
|
91
|
+
url.pathname += isDir ? '.json' : '/_jcr_content.json'
|
|
92
|
+
} else if (type === 'children') {
|
|
93
|
+
throw new Error('Cant load children of an asset node')
|
|
94
|
+
} else if (type === 'content' && isDir) {
|
|
95
|
+
throw new Error('Cant load content of an asset node')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return type === 'path' ? url.pathname : url.href
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Normalize AEM reference
|
|
103
|
+
* @param {URL} url
|
|
104
|
+
* @param {string} type
|
|
105
|
+
* @returns {URL}
|
|
106
|
+
*/
|
|
107
|
+
function normalizeReference(url, type) {
|
|
108
|
+
|
|
109
|
+
if (type === 'path') {
|
|
110
|
+
throw new Error('Cant determine path of a reference node directly')
|
|
111
|
+
} else if (type === 'meta') {
|
|
112
|
+
url.pathname = url.pathname.replace(/\/image\.img\.\w+\//, '/image.json/')
|
|
113
|
+
} else if (type === 'children') {
|
|
114
|
+
throw new Error('Cant load children of a reference node')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return url.href
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = exports = normalize
|
package/test.html
ADDED
package/webpack.config.js
CHANGED
|
@@ -2,7 +2,7 @@ const path = require('path');
|
|
|
2
2
|
|
|
3
3
|
module.exports = {
|
|
4
4
|
mode: 'production',
|
|
5
|
-
entry: './src/index.
|
|
5
|
+
entry: './src/index.js',
|
|
6
6
|
output: {
|
|
7
7
|
path: path.resolve(__dirname, './dist/'),
|
|
8
8
|
filename: 'ca.js',
|
|
@@ -21,5 +21,5 @@ module.exports = {
|
|
|
21
21
|
commonjs2: 'cross-fetch',
|
|
22
22
|
root: 'fetch'
|
|
23
23
|
}
|
|
24
|
-
}
|
|
24
|
+
}
|
|
25
25
|
}
|
package/src/index.mjs
DELETED
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
import fetch from 'cross-fetch'
|
|
2
|
-
import Bottleneck from 'bottleneck/light.js'
|
|
3
|
-
|
|
4
|
-
const domain = 'www.canada.ca'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Map month name to index
|
|
8
|
-
*/
|
|
9
|
-
const months = {
|
|
10
|
-
'Jan': '01',
|
|
11
|
-
'Feb': '02',
|
|
12
|
-
'Mar': '03',
|
|
13
|
-
'Apr': '04',
|
|
14
|
-
'May': '05',
|
|
15
|
-
'Jun': '06',
|
|
16
|
-
'Jul': '07',
|
|
17
|
-
'Aug': '08',
|
|
18
|
-
'Sep': '09',
|
|
19
|
-
'Oct': '10',
|
|
20
|
-
'Nov': '11',
|
|
21
|
-
'Dec': '12'
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Parse date in RFC1123 format
|
|
26
|
-
* @param {string} date
|
|
27
|
-
* @returns {Number}
|
|
28
|
-
*/
|
|
29
|
-
function maybeParseDate(date) {
|
|
30
|
-
// Basic
|
|
31
|
-
if (/^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
|
32
|
-
return Date.parse(date)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// RFC1123
|
|
36
|
-
let m = /^\w{3} (\w{3}) (\d{2}) (\d{4}) ([\d:]{8}) GMT([\-+]\d{4})$/.exec(date)
|
|
37
|
-
return m ? Date.parse(m[3] + '-' + months[m[1]] + '-' + m[2] + 'T' + m[4] + m[5]) : date
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Throttles requests to prevent being throttled by the server
|
|
42
|
-
*/
|
|
43
|
-
export const limiter = new Bottleneck({
|
|
44
|
-
reservoir: 150,
|
|
45
|
-
reservoirRefreshAmount: 150,
|
|
46
|
-
reservoirRefreshInterval: 5000,
|
|
47
|
-
maxConcurrent: 10,
|
|
48
|
-
trackDoneStatus: true
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Format fetch URL
|
|
53
|
-
* @param {string} path Node path
|
|
54
|
-
* @param {string} suffix
|
|
55
|
-
* @returns {string}
|
|
56
|
-
*/
|
|
57
|
-
function formatURL(path, suffix) {
|
|
58
|
-
return 'https://' + domain + path + suffix + '?_=' + Date.now()
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Normalize AEM path from URL
|
|
63
|
-
* @param {string} url
|
|
64
|
-
* @throws {Error} Invalid path
|
|
65
|
-
* @returns {string}
|
|
66
|
-
*/
|
|
67
|
-
export function normalizePath(url) {
|
|
68
|
-
url = new URL(url, 'https://' + domain)
|
|
69
|
-
|
|
70
|
-
if (url.hostname !== domain) {
|
|
71
|
-
throw new Error('Invalid hostname ' + url.hostname)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (url.pathname.startsWith('/content/dam/')) {
|
|
75
|
-
return url.pathname
|
|
76
|
-
} else if (url.pathname.startsWith('/content/canadasite/')) {
|
|
77
|
-
url.pathname = url.pathname.substr(19)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (/^\/(en|fr)(\/|$)/.test(url.pathname)) {
|
|
81
|
-
return url.pathname.replace(/\.[^\.]+$/, '').replace(/\/$/, '')
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
throw new Error('Invalid path ' + url)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Normalize node object (Modifies existing node if provided)
|
|
89
|
-
* @param {string|Object} node
|
|
90
|
-
* @throws {Error} Invalid node
|
|
91
|
-
* @returns {Object}
|
|
92
|
-
*/
|
|
93
|
-
export function normalizeNode(node) {
|
|
94
|
-
if (typeof node === 'string') {
|
|
95
|
-
return { path: normalizePath(node) }
|
|
96
|
-
} else if (typeof node === 'object' && typeof node.path === 'string') {
|
|
97
|
-
node.path = normalizePath(node.path)
|
|
98
|
-
return node
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
throw new Error('Invalid node')
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Verify response properties
|
|
106
|
-
* @param {Response} response
|
|
107
|
-
* @throws {Error} Bad response
|
|
108
|
-
* @returns {Response}
|
|
109
|
-
*/
|
|
110
|
-
function verifyResponse(response) {
|
|
111
|
-
if (!response.ok) {
|
|
112
|
-
throw new Error(response.statusText)
|
|
113
|
-
} else if (!response.url.includes(domain)) {
|
|
114
|
-
throw new Error('Redirect')
|
|
115
|
-
} else if (response.url.includes('/404.html')) {
|
|
116
|
-
throw new Error('Not Found')
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return response
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Get node children from XML sitemap
|
|
124
|
-
* @param {string|Object} node
|
|
125
|
-
* @returns {Promise<Object>}
|
|
126
|
-
*/
|
|
127
|
-
export function children(node) {
|
|
128
|
-
node = normalizeNode(node)
|
|
129
|
-
|
|
130
|
-
return limiter.schedule({ expiration: 60000, priority: 4 }, url => fetch(url), formatURL(node.path, '.sitemap.xml'))
|
|
131
|
-
.then(verifyResponse)
|
|
132
|
-
.then(response => response.text())
|
|
133
|
-
.then(xml => {
|
|
134
|
-
// Parse XML
|
|
135
|
-
node.children = xml.match(/<url>.*?<\/url>/g).map(url => {
|
|
136
|
-
let loc = url.match(/<loc>([^<]+)<\/loc>/)
|
|
137
|
-
let mod = url.match(/<lastmod>([^<]+)<\/lastmod>/)
|
|
138
|
-
return {
|
|
139
|
-
path: normalizePath(loc[1]),
|
|
140
|
-
lastmod: mod ? Date.parse(mod[1]) : null
|
|
141
|
-
}
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
// First entry may be the input node
|
|
145
|
-
if (node.children.length && node.children[0].path === node.path) {
|
|
146
|
-
node.lastmod = node.children.shift().lastmod
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return node
|
|
150
|
-
})
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Get node metadata from jcr content
|
|
155
|
-
* @param {string|Object} node
|
|
156
|
-
* @returns {Promise<Object>}
|
|
157
|
-
*/
|
|
158
|
-
export function meta(node) {
|
|
159
|
-
node = normalizeNode(node)
|
|
160
|
-
|
|
161
|
-
return limiter.schedule({ expiration: 30000 }, url => fetch(url), formatURL(node.path, '/jcr:content.json'))
|
|
162
|
-
.then(verifyResponse)
|
|
163
|
-
.then(response => response.json())
|
|
164
|
-
.then(meta => {
|
|
165
|
-
// Reformat some meta properties
|
|
166
|
-
Object.keys(meta).forEach(key => {
|
|
167
|
-
if (meta[key] === 'true') {
|
|
168
|
-
meta[key] = true
|
|
169
|
-
} else if (meta[key] === 'false') {
|
|
170
|
-
meta[key] = false
|
|
171
|
-
} else if (key.endsWith('@TypeHint')) {
|
|
172
|
-
delete meta[key]
|
|
173
|
-
} else if (typeof meta[key] === 'string') {
|
|
174
|
-
meta[key] = maybeParseDate(meta[key])
|
|
175
|
-
}
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
node.meta = meta
|
|
179
|
-
return node
|
|
180
|
-
})
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Get node content
|
|
185
|
-
* @param {string|Object} node
|
|
186
|
-
* @returns {Promise<Object>}
|
|
187
|
-
*/
|
|
188
|
-
export function content(node) {
|
|
189
|
-
node = normalizeNode(node)
|
|
190
|
-
let suffix = node.path.startsWith('/content/dam/') ? '' : '.html'
|
|
191
|
-
|
|
192
|
-
return limiter.schedule({ expiration: 30000 }, url => fetch(url), formatURL(node.path, suffix))
|
|
193
|
-
.then(verifyResponse)
|
|
194
|
-
.then(response => {
|
|
195
|
-
if (response.headers.get('Content-Type').includes('application/json')) {
|
|
196
|
-
return response.json()
|
|
197
|
-
}
|
|
198
|
-
return response.text()
|
|
199
|
-
})
|
|
200
|
-
.then(content => {
|
|
201
|
-
node.content = content
|
|
202
|
-
return node
|
|
203
|
-
})
|
|
204
|
-
}
|