wiki-plugin-mech 0.1.18 → 0.1.20
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/client/mech.js +126 -5
- package/package.json +1 -1
- package/server/server.js +52 -13
package/client/mech.js
CHANGED
|
@@ -519,11 +519,22 @@
|
|
|
519
519
|
// http://localhost:3000/plugin/mech/run/testing-mechs-synchronization/5e269010fc81aebe?args=WyJoZWxsbyIsIndvcmxkIl0
|
|
520
520
|
async function get_emit({elem,command,args,body,state}) {
|
|
521
521
|
if (!body) return trouble(elem,`GET expects indented commands to run on the server.`)
|
|
522
|
-
|
|
522
|
+
let share = {}
|
|
523
|
+
let where = state.context.site
|
|
524
|
+
if (args.length) {
|
|
525
|
+
for(const arg of args) {
|
|
526
|
+
if (arg in state) {
|
|
527
|
+
inspect(elem,arg,state)
|
|
528
|
+
share[arg] = state[arg]}
|
|
529
|
+
else if (arg.match(/\./)) where=arg
|
|
530
|
+
else {return trouble(elem,`GET expected "${arg}" to name state or site.`)}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
// const site = state.context.site
|
|
523
534
|
const slug = state.context.slug
|
|
524
535
|
const itemId = state.context.itemId
|
|
525
|
-
const query = `
|
|
526
|
-
const url = `//${
|
|
536
|
+
const query = `mech=${btoa(JSON.stringify(body))}&state=${btoa(JSON.stringify(share))}`
|
|
537
|
+
const url = `//${where}/plugin/mech/run/${slug}/${itemId}?${query}`
|
|
527
538
|
elem.innerHTML = command + ` ⇒ in progress`
|
|
528
539
|
const start = Date.now()
|
|
529
540
|
let result
|
|
@@ -534,15 +545,45 @@
|
|
|
534
545
|
return trouble(elem,`RUN failed with "${err.message}"`)
|
|
535
546
|
}
|
|
536
547
|
state.result = result
|
|
537
|
-
for(const arg of result.
|
|
548
|
+
for(const arg of result.mech.flat(9)){
|
|
538
549
|
const elem = document.getElementById(arg.key)
|
|
539
550
|
if('status' in arg) elem.innerHTML = arg.command + ` ⇒ ${arg.status}`
|
|
540
551
|
if('trouble' in arg) trouble(elem,arg.trouble)
|
|
541
552
|
}
|
|
553
|
+
if('debug' in result.state) delete result.state.debug
|
|
554
|
+
Object.assign(state,result.state)
|
|
542
555
|
const elapsed = ((Date.now() - start)/1000).toFixed(3)
|
|
543
556
|
elem.innerHTML = command + ` ⇒ ${elapsed} seconds`
|
|
544
557
|
}
|
|
545
558
|
|
|
559
|
+
function delta_emit({elem,command,args,body,state}) {
|
|
560
|
+
const copy = obj => JSON.parse(JSON.stringify(obj))
|
|
561
|
+
const size = obj => JSON.stringify(obj).length
|
|
562
|
+
if (args.length < 1) return trouble(elem,`DELTA expects argument, "have" or "apply" on client.`)
|
|
563
|
+
if (body) return trouble(elem,`DELTA doesn't expect indented input.`)
|
|
564
|
+
switch (args[0]) {
|
|
565
|
+
case 'have':
|
|
566
|
+
const edits = state.context.page.journal
|
|
567
|
+
.filter(item => item.type != 'fork')
|
|
568
|
+
state.recent = edits[edits.length-1].date
|
|
569
|
+
elem.innerHTML = command + ` ⇒ ${new Date(state.recent).toLocaleString()}`
|
|
570
|
+
break
|
|
571
|
+
case 'apply':
|
|
572
|
+
if(!('actions' in state)) return trouble(elem,`DELTA apply expect "actions" as input.`)
|
|
573
|
+
inspect(elem,'actions',state)
|
|
574
|
+
const page = copy(state.context.page)
|
|
575
|
+
const before = size(page)
|
|
576
|
+
for (const action of state.actions)
|
|
577
|
+
apply(page,action)
|
|
578
|
+
state.page = page
|
|
579
|
+
const after = size(page)
|
|
580
|
+
elem.innerHTML = command + ` ⇒ ∆ ${((after-before)/before*100).toFixed(1)}%`
|
|
581
|
+
break
|
|
582
|
+
default:
|
|
583
|
+
trouble(elem,`DELTA doesn't know "${args[0]}".`)
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
546
587
|
|
|
547
588
|
// C A T A L O G
|
|
548
589
|
|
|
@@ -566,7 +607,8 @@
|
|
|
566
607
|
RANDOM: {emit:random_emit},
|
|
567
608
|
SLEEP: {emit:sleep_emit},
|
|
568
609
|
TOGETHER:{emit:together_emit},
|
|
569
|
-
GET: {emit:get_emit}
|
|
610
|
+
GET: {emit:get_emit},
|
|
611
|
+
DELTA: {emit:delta_emit}
|
|
570
612
|
}
|
|
571
613
|
|
|
572
614
|
|
|
@@ -792,4 +834,83 @@
|
|
|
792
834
|
return this.direction}
|
|
793
835
|
}
|
|
794
836
|
|
|
837
|
+
// adapted from wiki-client/lib/revision.coffee
|
|
838
|
+
|
|
839
|
+
// This module interprets journal actions in order to update
|
|
840
|
+
// a story or even regenerate a complete story from some or
|
|
841
|
+
// all of a journal.
|
|
842
|
+
|
|
843
|
+
function apply(page, action) {
|
|
844
|
+
const order = () => {
|
|
845
|
+
return (page.story || []).map(item => item?.id);
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
const add = (after, item) => {
|
|
849
|
+
const index = order().indexOf(after) + 1;
|
|
850
|
+
page.story.splice(index, 0, item);
|
|
851
|
+
};
|
|
852
|
+
|
|
853
|
+
const remove = () => {
|
|
854
|
+
const index = order().indexOf(action.id);
|
|
855
|
+
if (index !== -1) {
|
|
856
|
+
page.story.splice(index, 1);
|
|
857
|
+
}
|
|
858
|
+
};
|
|
859
|
+
|
|
860
|
+
page.story = page.story || [];
|
|
861
|
+
|
|
862
|
+
switch (action.type) {
|
|
863
|
+
case 'create':
|
|
864
|
+
if (action.item) {
|
|
865
|
+
if (action.item.title != null) {
|
|
866
|
+
page.title = action.item.title;
|
|
867
|
+
}
|
|
868
|
+
if (action.item.story != null) {
|
|
869
|
+
page.story = action.item.story.slice();
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
break;
|
|
873
|
+
case 'add':
|
|
874
|
+
add(action.after, action.item);
|
|
875
|
+
break;
|
|
876
|
+
case 'edit':
|
|
877
|
+
const index = order().indexOf(action.id);
|
|
878
|
+
if (index !== -1) {
|
|
879
|
+
page.story.splice(index, 1, action.item);
|
|
880
|
+
} else {
|
|
881
|
+
page.story.push(action.item);
|
|
882
|
+
}
|
|
883
|
+
break;
|
|
884
|
+
case 'move':
|
|
885
|
+
// construct relative addresses from absolute order
|
|
886
|
+
const moveIndex = action.order.indexOf(action.id);
|
|
887
|
+
const after = action.order[moveIndex - 1];
|
|
888
|
+
const item = page.story[order().indexOf(action.id)];
|
|
889
|
+
remove();
|
|
890
|
+
add(after, item);
|
|
891
|
+
break;
|
|
892
|
+
case 'remove':
|
|
893
|
+
remove();
|
|
894
|
+
break;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
page.journal = page.journal || [];
|
|
898
|
+
if (action.fork) {
|
|
899
|
+
// implicit fork
|
|
900
|
+
page.journal.push({ type: 'fork', site: action.fork, date: action.date - 1 });
|
|
901
|
+
}
|
|
902
|
+
page.journal.push(action);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
function create(revIndex, data) {
|
|
906
|
+
revIndex = +revIndex;
|
|
907
|
+
const revJournal = data.journal.slice(0, revIndex + 1);
|
|
908
|
+
const revPage = { title: data.title, story: [] };
|
|
909
|
+
for (const action of revJournal) {
|
|
910
|
+
apply(revPage, action || {});
|
|
911
|
+
}
|
|
912
|
+
return revPage;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
|
|
795
916
|
}).call(this)
|
package/package.json
CHANGED
package/server/server.js
CHANGED
|
@@ -5,28 +5,29 @@
|
|
|
5
5
|
(function() {
|
|
6
6
|
|
|
7
7
|
const fs = require('fs')
|
|
8
|
+
const path = require('path')
|
|
8
9
|
const process = require('process')
|
|
9
10
|
|
|
11
|
+
function cors (req, res, next) {
|
|
12
|
+
res.header('Access-Control-Allow-Origin', '*')
|
|
13
|
+
next()
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
function startServer(params) {
|
|
11
17
|
var app = params.app,
|
|
12
18
|
argv = params.argv
|
|
13
19
|
|
|
14
|
-
return app.get('/plugin/mech/run/:slug([a-z-]+)/:itemId', (req, res, next) => {
|
|
20
|
+
return app.get('/plugin/mech/run/:slug([a-z-]+)/:itemId', cors, (req, res, next) => {
|
|
15
21
|
console.log(req.params)
|
|
16
22
|
try {
|
|
17
23
|
const slug = req.params.slug
|
|
18
24
|
const itemId = req.params.itemId
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// })
|
|
26
|
-
const context = {path}
|
|
27
|
-
const state = {context,debug:true}
|
|
28
|
-
run(args,state)
|
|
29
|
-
.then(() => {return res.json({args,state})})
|
|
25
|
+
const mech = JSON.parse(atob(req.query.mech || 'W10='))
|
|
26
|
+
const share = JSON.parse(atob(req.query.state ||'W10='))
|
|
27
|
+
const context = {argv,slug}
|
|
28
|
+
const state = Object.assign(share,{context})
|
|
29
|
+
run(mech,state)
|
|
30
|
+
.then(() => {delete state.context; return res.json({mech,state})})
|
|
30
31
|
.catch(err => {console.log(err); return res.json({err:err.message+' from promise'})})
|
|
31
32
|
} catch(err) {
|
|
32
33
|
return res.json({err:err.message+' from try'})
|
|
@@ -98,13 +99,51 @@
|
|
|
98
99
|
})
|
|
99
100
|
}
|
|
100
101
|
|
|
102
|
+
async function commons_emit ({elem,args,state}) {
|
|
103
|
+
const readdir = dir => new Promise((res,rej) =>
|
|
104
|
+
fs.readdir(dir,(e,v) => e ? rej(e) : res(v)));
|
|
105
|
+
const stat = file => new Promise((res,rej) =>
|
|
106
|
+
fs.stat(file,(e,v) => e ? rej(e) : res(v)));
|
|
107
|
+
const tally = async dir => {
|
|
108
|
+
const count = {files:0,bytes:0}
|
|
109
|
+
const items = await readdir(dir)
|
|
110
|
+
for(const item of items) {
|
|
111
|
+
const itemPath = path.join(dir, item)
|
|
112
|
+
const stats = await stat(itemPath)
|
|
113
|
+
if (state.debug) console.log({itemPath,stats})
|
|
114
|
+
if (stats.isFile()) {
|
|
115
|
+
count.files++
|
|
116
|
+
count.bytes+=stats.size
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return count
|
|
120
|
+
}
|
|
121
|
+
const all = await tally(state.context.argv.commons)
|
|
122
|
+
const here = await tally(path.join(state.context.argv.data,'assets','plugins','image'))
|
|
123
|
+
state.commons = {all,here}
|
|
124
|
+
status(elem,`${(all.bytes/1000000).toFixed(3)} mb in ${all.files} files`)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function delta_emit ({elem,args,state}) {
|
|
128
|
+
const readFile = path => new Promise((res,rej) =>
|
|
129
|
+
fs.readFile(path,(e,v) => e ? rej(e) : res(v)));
|
|
130
|
+
if(!state.recent) return trouble(elem,`DELTA expects "recent" update time in state.`)
|
|
131
|
+
const file = path.join(state.context.argv.db,state.context.slug)
|
|
132
|
+
const page = JSON.parse(await readFile(file))
|
|
133
|
+
state.actions = page.journal
|
|
134
|
+
.filter(action => action.date > state.recent)
|
|
135
|
+
status(elem,`${state.actions.length} recent actions`)
|
|
136
|
+
}
|
|
137
|
+
|
|
101
138
|
|
|
102
139
|
// C A T A L O G
|
|
103
140
|
|
|
104
141
|
const blocks = {
|
|
105
142
|
HELLO: {emit:hello_emit},
|
|
106
143
|
UPTIME: {emit:uptime_emit},
|
|
107
|
-
SLEEP: {emit:sleep_emit}
|
|
144
|
+
SLEEP: {emit:sleep_emit},
|
|
145
|
+
COMMONS: {emit:commons_emit},
|
|
146
|
+
DELTA: {emit:delta_emit}
|
|
108
147
|
}
|
|
109
148
|
|
|
110
149
|
|