wiki-plugin-mech 0.1.19 → 0.1.21
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 +142 -4
- package/package.json +1 -1
- package/server/server.js +21 -8
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 = `mech=${btoa(JSON.stringify(body))}`
|
|
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
|
|
@@ -539,10 +550,56 @@
|
|
|
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
|
+
|
|
587
|
+
function roster_emit({elem,command,state}) {
|
|
588
|
+
if(!state.neighborhood) return trouble(elem,`ROSTER expected a neighborhood, like from NEIGHBORS.`)
|
|
589
|
+
inspect(elem,'neighborhood',state)
|
|
590
|
+
const infos = state.neighborhood
|
|
591
|
+
const sites = infos
|
|
592
|
+
.map(info => info.domain)
|
|
593
|
+
.filter(uniq)
|
|
594
|
+
const any = array => array[Math.floor(Math.random()*array.length)]
|
|
595
|
+
console.log(infos)
|
|
596
|
+
const items = [
|
|
597
|
+
{type:'roster', text:"Mech\n"+sites.join("\n")},
|
|
598
|
+
{type:'activity', text:`ROSTER Mech\nSINCE 30 days`}]
|
|
599
|
+
elem.innerHTML = command + ` ⇒ ${sites.length} sites`
|
|
600
|
+
state.items = items
|
|
601
|
+
}
|
|
602
|
+
|
|
546
603
|
|
|
547
604
|
// C A T A L O G
|
|
548
605
|
|
|
@@ -566,7 +623,9 @@
|
|
|
566
623
|
RANDOM: {emit:random_emit},
|
|
567
624
|
SLEEP: {emit:sleep_emit},
|
|
568
625
|
TOGETHER:{emit:together_emit},
|
|
569
|
-
GET: {emit:get_emit}
|
|
626
|
+
GET: {emit:get_emit},
|
|
627
|
+
DELTA: {emit:delta_emit},
|
|
628
|
+
ROSTER: {emit:roster_emit}
|
|
570
629
|
}
|
|
571
630
|
|
|
572
631
|
|
|
@@ -792,4 +851,83 @@
|
|
|
792
851
|
return this.direction}
|
|
793
852
|
}
|
|
794
853
|
|
|
854
|
+
// adapted from wiki-client/lib/revision.coffee
|
|
855
|
+
|
|
856
|
+
// This module interprets journal actions in order to update
|
|
857
|
+
// a story or even regenerate a complete story from some or
|
|
858
|
+
// all of a journal.
|
|
859
|
+
|
|
860
|
+
function apply(page, action) {
|
|
861
|
+
const order = () => {
|
|
862
|
+
return (page.story || []).map(item => item?.id);
|
|
863
|
+
};
|
|
864
|
+
|
|
865
|
+
const add = (after, item) => {
|
|
866
|
+
const index = order().indexOf(after) + 1;
|
|
867
|
+
page.story.splice(index, 0, item);
|
|
868
|
+
};
|
|
869
|
+
|
|
870
|
+
const remove = () => {
|
|
871
|
+
const index = order().indexOf(action.id);
|
|
872
|
+
if (index !== -1) {
|
|
873
|
+
page.story.splice(index, 1);
|
|
874
|
+
}
|
|
875
|
+
};
|
|
876
|
+
|
|
877
|
+
page.story = page.story || [];
|
|
878
|
+
|
|
879
|
+
switch (action.type) {
|
|
880
|
+
case 'create':
|
|
881
|
+
if (action.item) {
|
|
882
|
+
if (action.item.title != null) {
|
|
883
|
+
page.title = action.item.title;
|
|
884
|
+
}
|
|
885
|
+
if (action.item.story != null) {
|
|
886
|
+
page.story = action.item.story.slice();
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
break;
|
|
890
|
+
case 'add':
|
|
891
|
+
add(action.after, action.item);
|
|
892
|
+
break;
|
|
893
|
+
case 'edit':
|
|
894
|
+
const index = order().indexOf(action.id);
|
|
895
|
+
if (index !== -1) {
|
|
896
|
+
page.story.splice(index, 1, action.item);
|
|
897
|
+
} else {
|
|
898
|
+
page.story.push(action.item);
|
|
899
|
+
}
|
|
900
|
+
break;
|
|
901
|
+
case 'move':
|
|
902
|
+
// construct relative addresses from absolute order
|
|
903
|
+
const moveIndex = action.order.indexOf(action.id);
|
|
904
|
+
const after = action.order[moveIndex - 1];
|
|
905
|
+
const item = page.story[order().indexOf(action.id)];
|
|
906
|
+
remove();
|
|
907
|
+
add(after, item);
|
|
908
|
+
break;
|
|
909
|
+
case 'remove':
|
|
910
|
+
remove();
|
|
911
|
+
break;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
page.journal = page.journal || [];
|
|
915
|
+
if (action.fork) {
|
|
916
|
+
// implicit fork
|
|
917
|
+
page.journal.push({ type: 'fork', site: action.fork, date: action.date - 1 });
|
|
918
|
+
}
|
|
919
|
+
page.journal.push(action);
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function create(revIndex, data) {
|
|
923
|
+
revIndex = +revIndex;
|
|
924
|
+
const revJournal = data.journal.slice(0, revIndex + 1);
|
|
925
|
+
const revPage = { title: data.title, story: [] };
|
|
926
|
+
for (const action of revJournal) {
|
|
927
|
+
apply(revPage, action || {});
|
|
928
|
+
}
|
|
929
|
+
return revPage;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
|
|
795
933
|
}).call(this)
|
package/package.json
CHANGED
package/server/server.js
CHANGED
|
@@ -8,23 +8,24 @@
|
|
|
8
8
|
const path = require('path')
|
|
9
9
|
const process = require('process')
|
|
10
10
|
|
|
11
|
+
function cors (req, res, next) {
|
|
12
|
+
res.header('Access-Control-Allow-Origin', '*')
|
|
13
|
+
next()
|
|
14
|
+
}
|
|
15
|
+
|
|
11
16
|
function startServer(params) {
|
|
12
17
|
var app = params.app,
|
|
13
18
|
argv = params.argv
|
|
14
19
|
|
|
15
|
-
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) => {
|
|
16
21
|
console.log(req.params)
|
|
17
22
|
try {
|
|
18
23
|
const slug = req.params.slug
|
|
19
24
|
const itemId = req.params.itemId
|
|
20
25
|
const mech = JSON.parse(atob(req.query.mech || 'W10='))
|
|
21
|
-
|
|
22
|
-
// const page = JSON.parse(data)
|
|
23
|
-
// const item = page.story.find(item => item.id == itemId) || null
|
|
24
|
-
// return res.json({err,item,mech});
|
|
25
|
-
// })
|
|
26
|
+
const share = JSON.parse(atob(req.query.state ||'W10='))
|
|
26
27
|
const context = {argv,slug}
|
|
27
|
-
const state = {context}
|
|
28
|
+
const state = Object.assign(share,{context})
|
|
28
29
|
run(mech,state)
|
|
29
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'})})
|
|
@@ -123,6 +124,17 @@
|
|
|
123
124
|
status(elem,`${(all.bytes/1000000).toFixed(3)} mb in ${all.files} files`)
|
|
124
125
|
}
|
|
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
|
+
|
|
126
138
|
|
|
127
139
|
// C A T A L O G
|
|
128
140
|
|
|
@@ -130,7 +142,8 @@
|
|
|
130
142
|
HELLO: {emit:hello_emit},
|
|
131
143
|
UPTIME: {emit:uptime_emit},
|
|
132
144
|
SLEEP: {emit:sleep_emit},
|
|
133
|
-
COMMONS: {emit:commons_emit}
|
|
145
|
+
COMMONS: {emit:commons_emit},
|
|
146
|
+
DELTA: {emit:delta_emit}
|
|
134
147
|
}
|
|
135
148
|
|
|
136
149
|
|