Merge pull request #99 from R-JG/mast

Mast & Eyre channels in %neo
This commit is contained in:
will hanlen 2024-08-09 04:45:47 +09:00 committed by GitHub
commit 88364a5f40
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 1265 additions and 4 deletions

View File

@ -257,6 +257,7 @@
%noun (on-noun q.vase)
%neo-raw-poke (on-move (poke:harden !<(raw-poke:neo vase)))
%handle-http-request (handle-http-request:sttp !<([@ta inbound-request:eyre] vase))
%json (handle-eyre-chan-request:sttp !<(json vase))
==
++ on-noun
|= non=*
@ -322,6 +323,7 @@
[%sync rest=*] (on-peer-sync (pave:neo rest.pole) stop)
[%fetch rest=*] ?:(stop run (on-peer-fetch (pave:neo rest.pole)))
[%http-response *] run
[%eyre-chan *] run
==
::
++ on-peer-fetch
@ -2316,6 +2318,7 @@
%ack run
%eyre-req (on-eyre-req !<(req:eyre:neo q.pail.note))
%eyre-sign (on-eyre-sign src !<(sign:eyre:neo q.pail.note))
%eyre-chan-gift (on-eyre-chan-gift !<(chan-gift:eyre:neo q.pail.note))
==
+$ request-line
$: [ext=(unit @ta) site=(list @t)]
@ -2348,6 +2351,13 @@
%data `http-response-data/!>(dat.gift)
%done ~
==
::
++ on-eyre-chan-gift
|= [sub=path dat=json]
^+ run
=/ cag=cage [%json !>(dat)]
(give %fact ~[sub] cag)
::
++ match-binding
=| test=(list @t)
|= site=(list @t)
@ -2370,6 +2380,27 @@
=/ =card:neo [u.bin %poke eyre-task/!>(`task:eyre:neo`[eyre-id req])]
=/ =move:neo [#/[p/our.bowl]/$/eyre card]
(emit (do-move move))
::
++ handle-eyre-chan-request
|= jon=json
^+ run
=/ dst=(unit pith:neo)
?. &(?=(^ jon) ?=(%o -.jon))
~
=/ str=(unit json) (~(get by p.jon) 'pith')
?. &(?=(^ str) ?=(^ u.str) ?=(%s -.u.str))
~
[~ (pave:neo (stab p.u.str))]
?~ dst ~|(eyre-channel-req-missing-pith/jon !!)
=. jon
?. &(?=(^ jon) ?=(%o -.jon))
~
=/ dat=(unit json) (~(get by p.jon) 'data')
?~ dat ~
u.dat
=/ =card:neo [u.dst %poke eyre-chan-task/!>(`chan-task:eyre:neo`jon)]
=/ =move:neo [#/[p/our.bowl]/$/eyre card]
(emit (do-move move))
--
++ cttp

View File

@ -0,0 +1,274 @@
let rope;
let pith;
let path;
let ship;
let app;
let channelMessageId;
let subscriptionId;
let eventSource;
const channelId = `${Date.now()}${Math.floor(Math.random() * 100)}`;
const channelPath = `${window.location.origin}/~/channel/${channelId}`;
addEventListener('DOMContentLoaded', async () => {
channelMessageId = 0;
rope = Number(document.documentElement.getAttribute('rope'));
pith = document.documentElement.getAttribute('pith');
path = document.documentElement.getAttribute('path');
ship = document.documentElement.getAttribute('ship');
app = document.documentElement.getAttribute('app');
await connectToShip();
let eventElements = document.querySelectorAll('[event]');
eventElements.forEach(el => setEventListeners(el));
});
async function connectToShip() {
const storageKey = `${rope}${ship}`;
let storedStr = localStorage.getItem(storageKey);
localStorage.setItem(storageKey, `${channelMessageId} ${channelId}`);
if (storedStr) {
const storedIds = storedStr.split(' ');
const oldPath = `${window.location.origin}/~/channel/${storedIds[1]}`;
fetch(oldPath, {
method: 'PUT',
body: JSON.stringify([
{
id: channelMessageId,
action: 'unsubscribe',
subscription: Number(storedIds[0])
},
{
id: channelMessageId++,
action: 'delete'
}
])
});
};
await fetch(channelPath, {
method: 'PUT',
body: JSON.stringify(makeSubscribeBody(channelMessageId))
});
eventSource = new EventSource(channelPath);
eventSource.addEventListener('message', handleChannelStream);
};
function setEventListeners(el) {
const eventAttrVals = el.getAttribute('event');
const returnAttrVals = el.getAttribute('return');
const throttleMs = Number(el.getAttribute('throttle')) * 1000;
const debounceMs = Number(el.getAttribute('debounce')) * 1000;
eventAttrVals.split(/\s+/).forEach(eventAttr => {
let splitEventAttr = eventAttr.split('/');
if (splitEventAttr[0] === '') splitEventAttr.shift();
const eventType = splitEventAttr[0];
if (throttleMs) {
el[`on${eventType}`] = pokeThrottle(throttleMs, eventType, eventAttr, returnAttrVals);
} else if (debounceMs) {
el[`on${eventType}`] = pokeDebounce(debounceMs, eventType, eventAttr, returnAttrVals);
} else {
el[`on${eventType}`] = (e) => pokeShip(e, e.currentTarget, eventType, eventAttr, returnAttrVals);
};
});
};
function pokeThrottle(ms, ...pokeArgs) {
let ready = true;
return (e) => {
if (!ready) return;
ready = false;
window.setTimeout(() => { ready = true; }, ms);
pokeShip(e, e.currentTarget, ...pokeArgs);
};
};
function pokeDebounce(ms, ...pokeArgs) {
let timeoutId = null;
return (e) => {
window.clearTimeout(timeoutId);
timeoutId = window.setTimeout(() => pokeShip(e, e.target, ...pokeArgs), ms);
};
};
function pokeShip(event, target, eventType, eventAttr, returnAttrVals) {
const jsOnEvent = target.getAttribute('js-on-event');
if (jsOnEvent) {
eval?.(`"use strict"; ${jsOnEvent}`);
};
let uiEventData = {};
if (returnAttrVals) {
uiEventData = handleReturnAttr(event, target, returnAttrVals);
};
if (eventType === 'submit') {
event.preventDefault();
const formData = new FormData(target);
formData.forEach((v, k) => { uiEventData[k] = v });
target.reset();
};
fetch(channelPath, {
method: 'PUT',
body: JSON.stringify(makePokeBody({
rope,
path: eventAttr,
data: uiEventData
}))
});
};
function handleReturnAttr(event, target, returnAttrVals) {
let returnData = {};
returnAttrVals.split(/\s+/).forEach(returnAttr => {
let splitReturnAttr = returnAttr.split('/');
if (splitReturnAttr[0] === '') splitReturnAttr.shift();
const returnObjSelector = splitReturnAttr[0];
const key = splitReturnAttr[1];
if (returnObjSelector === 'event') {
if (!(key in event)) {
console.error(`Property: ${key} does not exist on the event object`);
return;
};
returnData[returnAttr] = String(event[key]);
} else {
let returnObj;
if (returnObjSelector === 'target') {
returnObj = target;
} else {
const linkedEl = document.getElementById(returnObjSelector);
if (!linkedEl) {
console.error(`No element found for id: ${returnObjSelector}`);
return;
};
returnObj = linkedEl;
};
if (key.startsWith('data')) {
const dataKey = key.substring(5).split('-').map((w, i) => {
if (i === 0) {
return w.toLowerCase();
} else {
return w.charAt(0).toUpperCase() + w.slice(1).toLowerCase();
};
}).join('');
if (!returnObj.dataset.hasOwnProperty(dataKey)) {
console.error(`Property: ${dataKey} does not exist on the specified object`);
return;
};
returnData[returnAttr] = String(returnObj.dataset[dataKey]);
} else {
if (!(key in returnObj)) {
console.error(`Property: ${key} does not exist on the specified object`);
return;
};
// TODO: handle other properties that don't cast to string
returnData[returnAttr] = String(returnObj[key]);
};
};
});
return returnData;
};
function handleChannelStream(event) {
const streamResponse = JSON.parse(event.data);
fetch(channelPath, {
method: 'PUT',
body: JSON.stringify(makeAck(streamResponse.id))
});
if (streamResponse.response !== 'diff') return;
const gust = streamResponse.json;
if (!gust) return;
// console.log(gust);
gust.forEach(gustObj => {
switch (gustObj.p) {
case 'd':
gustObj.q.forEach(key => {
let toRemove = document.querySelector(`[key="${key}"]`)
const jsOnDelete = toRemove.getAttribute('js-on-delete');
if (jsOnDelete) {
eval?.(`"use strict"; ${jsOnDelete}`);
};
toRemove.remove();
});
break;
case 'n':
let parent = document.querySelector(`[key="${gustObj.q}"]`);
if (gustObj.r === 0) {
parent.insertAdjacentHTML('afterbegin', gustObj.s);
} else if (gustObj.r === parent.childNodes.length) {
parent.insertAdjacentHTML('beforeend', gustObj.s);
} else {
let indexTarget = parent.childNodes[gustObj.r];
if (indexTarget.nodeType === 1) {
indexTarget.insertAdjacentHTML('beforebegin', gustObj.s);
} else {
let placeholder = document.createElement('div');
parent.insertBefore(placeholder, indexTarget);
placeholder = parent.childNodes[gustObj.r];
placeholder.outerHTML = gustObj.s;
};
};
let newNode = parent.childNodes[gustObj.r];
if (newNode.getAttribute('event')) {
setEventListeners(newNode);
};
if (newNode.childElementCount > 0) {
let needingListeners = newNode.querySelectorAll('[event]');
needingListeners.forEach(child => setEventListeners(child));
};
const jsOnAdd = newNode.getAttribute('js-on-add');
if (jsOnAdd) {
eval?.(`"use strict"; ${jsOnAdd}`);
};
break;
case 'm':
let fromNode = document.querySelector(`[key="${gustObj.q}"]`);
const fromIndex = [ ...fromNode.parentNode.childNodes ].indexOf(fromNode);
if (fromIndex < gustObj.r) gustObj.r++;
let toNode = fromNode.parentNode.childNodes[gustObj.r];
fromNode.parentNode.insertBefore(fromNode, toNode);
break;
case 'c':
let targetNode = document.querySelector(`[key="${gustObj.q}"]`);
if (gustObj.r.length) {
gustObj.r.forEach(attr => {
if (attr === 'event') {
let eventVal = targetNode.getAttribute('event').split('/');
if (eventVal[0] === '') eventVal.shift();
const eventType = eventVal[0];
targetNode[`on${eventType}`] = null;
};
targetNode.removeAttribute(attr);
});
};
if (gustObj.s.length) {
gustObj.s.forEach(attr => {
const name = attr[0];
const value = attr[1];
targetNode.setAttribute(name, value);
if (name === 'event') setEventListeners(targetNode);
});
};
break;
case 't':
let textWrapperNode = document.querySelector(`[key="${gustObj.q}"]`);
textWrapperNode.textContent = gustObj.r;
break;
};
});
};
function makeSubscribeBody(channelMessageId) {
return [{
id: channelMessageId,
action: 'subscribe',
ship: ship,
app: app,
path: path
}];
};
function makePokeBody(jsonData) {
channelMessageId++;
return [{
id: channelMessageId,
action: 'poke',
ship: ship,
app: app,
mark: 'json',
json: { pith: pith, data: jsonData }
}];
};
function makeAck(eventId) {
channelMessageId++;
return [{
id: channelMessageId,
action: 'ack',
"event-id": eventId
}];
};

View File

@ -0,0 +1 @@
~

View File

@ -0,0 +1,199 @@
/@ ui-event
/@ txt
/@ diary-diff
^- kook:neo
=<
|%
++ state pro/%manx
++ poke (sy %ui-event %rely %gift ~)
++ kids
*kids:neo
:: ^- kids:neo
:: :+ ~ %y
:: %- my
:: :~ [[&/%selection |] pro/%sig ~]
:: ==
++ deps
^- deps:neo
%- my
:~ :^ %src & [pro/%diary (sy %diary-diff ~)]
:+ ~ %y
%- my
:~ [[|/%da |] only/%txt ~]
==
==
++ form
^- form:neo
|_ [=bowl:neo =aeon:neo =pail:neo]
::
++ init
|= pal=(unit pail:neo)
^- (quip card:neo pail:neo)
:- ~
manx/!>((render (get-render-data bowl)))
::
++ poke
|= [sud=stud:neo vaz=vase]
^- (quip card:neo pail:neo)
?+ sud ~|(bad-stud/sud !!)
::
%ui-event
=/ eve !<(ui-event vaz)
?+ path.eve ~|(missing-event-handler-for/path.eve !!)
::
[%submit %diary-form ~]
=/ dat=@t (~(got by data.eve) 'diary-input')
=/ dif=diary-diff [%put-entry now.bowl dat]
=/ dst=pith:neo p:(~(got by deps.bowl) %src)
:_ pail
:~ [dst %poke diary-diff/!>(dif)]
==
::
[%click %delete @ta ~]
=/ key=@da (slav %da i.t.t.path.eve)
=/ dif=diary-diff [%del-entry key]
=/ dst=pith:neo p:(~(got by deps.bowl) %src)
:_ pail
:~ [dst %poke diary-diff/!>(dif)]
==
::
==
::
%rely
`manx/!>((render (get-render-data bowl)))
::
==
::
--
--
::
|%
::
+$ render-data
$: diary-entries=(list [date=@da =txt])
selection=(unit @da)
==
::
++ render
|_ render-data
::
++ $
^- manx
;html
;head
;meta(charset "utf-8");
;script
;+ ;/
%- trip
'''
function setLoading(idStr) {
let target = document.getElementById(idStr);
target.className = 'loading';
};
function setLoaded(idStr) {
let target = document.getElementById(idStr);
target.className = 'loaded';
};
'''
==
;style
;+ ;/
%- trip
'''
.loading {
background-color: orange;
}
.loaded {
background-color: green;
}
'''
==
==
;+ body
==
::
++ body
^- manx
;body
=style "margin: 0; width: 100%; display: grid; place-items: center;"
;main
;h1: Diary
;+ diary-form
;+ diary-items
==
==
::
++ diary-form
^- manx
;form
=event "/submit/diary-form"
=js-on-event "setLoading('form-button');"
;textarea
=name "diary-input"
=placeholder "Today, I ..."
=style "height: 10rem; width: 25rem; margin-block: 1rem;"
;* ~
==
;button#form-button.loaded: Enter
==
::
++ diary-items
^- manx
;div
;* %+ turn diary-entries
|= [date=@da =txt]
=/ key=tape <date>
;div
=key key
=js-on-add "setLoaded('form-button');"
;p: {(pretty-date date)}
;p: {(trip txt)}
;button.loaded
=event "/click/delete/{key}"
=js-on-event "setLoading('{key}');"
=id key
;+ ;/ "✖"
==
==
==
::
--
:: :: :: :: :: :: :: :: :: ::
++ get-render-data
|= =bowl:neo
^- render-data
:* (get-diary-entries deps.bowl)
(get-selection kids.bowl)
==
::
++ get-diary-entries
|= deps=(map term (pair pith:neo lore:neo))
^- (list [date=@da =txt])
=/ data=(unit (pair pith:neo lore:neo))
(~(get by deps) %src)
?~ data ~|(%no-diary !!)
=/ entries=(list [date=@da =txt])
%+ turn ~(tap by kid.q.u.data)
|= (pair iota:neo (axal:neo idea:neo))
?> &(?=(^ p) ?=(%da -.p) ?=(^ fil.q))
[+.p !<(txt q.pail.u.fil.q)]
%+ sort entries
|= (pair [date=@da =txt] [date=@da =txt])
(gth date.p date.q)
::
++ get-selection
|= kids=(axal:neo idea:neo)
^- (unit @da)
=/ data=(unit (axal:neo idea:neo))
(~(get by kid.kids) %selection)
?~ data ~
?~ fil.u.data ~
[~ !<(@da q.pail.u.fil.u.data)]
::
++ pretty-date :: from diary-htmx
|= date=@da
^- tape
=/ d (yore date)
"{(y-co:co y:d)}-{(y-co:co m:d)}-{(y-co:co d:t:d)}"
::
--

View File

@ -0,0 +1,141 @@
/@ ui-event
/@ task
/@ task-diff
^- kook:neo
=<
|%
++ state pro/%manx
++ poke (sy %ui-event %rely %gift ~)
++ kids
*kids:neo
++ deps
^- deps:neo
%- my
:~ :- %src
:- req=&
:- [pro/%task (sy %task-diff %gift ~)]
:+ ~ %y
%- my
:~ [[|/%ud |] pro/%task (sy %task-diff %gift ~)]
==
==
++ form
^- form:neo
|_ [=bowl:neo =aeon:neo =pail:neo]
::
++ init
|= pal=(unit pail:neo)
^- (quip card:neo pail:neo)
:- ~
manx/!>((render (task-map bowl)))
::
++ poke
|= [=stud:neo =vase]
^- (quip card:neo pail:neo)
?+ stud ~|(bad-stud/stud !!)
::
%ui-event
=/ event !<(ui-event vase)
?+ path.event
~|(missing-event-handler-for/path.event !!)
::
[%submit %new-task ~]
=/ text=@t
(~(got by data.event) 'task-input')
:_ pail
:~ :- p:(~(got by deps.bowl) %src)
:+ %poke
%task-diff
!>([%new [text %.y %.y ~] %.y])
==
::
[%click %checkbox * ~]
=/ inner (oust [0 2] (pave:neo path.event))
=/ =lore:neo q:(~(got by deps.bowl) %src)
=/ =idea:neo (~(got of:neo lore) inner)
=/ t !<(task q.pail.idea)
:_ pail
:~ :- (welp p:(~(got by deps.bowl) %src) inner)
:+ %poke
%task-diff
!>([%edit text.t !done.t])
==
==
::
%rely
:- ~
manx/!>((render (task-map bowl)))
==
--
--
::
|%
++ render
|_ tasks=(map pith task)
++ $
^- manx
;html
;head
;meta(charset "utf-8");
==
;body
=style "margin: 0; width: 100%; display: grid; place-items: center;"
;main
;h1: Tasks
;div
;p: {(trip text:(~(got by tasks) /))}
==
;+ task-form
;+ subtasks
==
==
==
::
++ task-form
^- manx
;form
=event "/submit/new-task"
;textarea(name "task-input", style "height: 10rem; width: 25rem; margin-block: 1rem;");
;button: Enter
==
::
++ subtasks
^- manx
;div
;* %+ turn
%~ tap by
(~(del by tasks) /)
|= [=pith =task]
=/ key (en-tape:pith:neo pith)
;div
;p: {(trip text.task)}
::
;+
=; m
?: done.task
m(a.g [[%checked ""] a.g.m])
m
^- manx
;input
=id "task-checkbox"
=type "checkbox"
=name "done"
=event (welp "/click/checkbox" key)
;
==
==
==
--
::
++ task-map
|= =bowl:neo
^- (map pith task)
%- malt
%+ turn
%~ tap
of:neo
q:(~(got by deps.bowl) %src)
|= [=pith =idea:neo]
:- pith
!<(task q.pail.idea)
--

View File

@ -0,0 +1,601 @@
/@ ui-event
/@ mast-bind
/* mast-js
=<
^- kook:neo
|%
++ state [%pro %sig]
++ poke (sy %mast-bind %eyre-task %eyre-chan-task %gift ~)
++ deps *deps:neo
++ kids
:+ ~ %y
%- my
:~ [[|/%ud |/%p |] [%pro %manx] (sy %ui-event %rely ~)]
==
++ form
^- form:neo
|_ [=bowl:neo =aeon:neo =pail:neo]
::
++ init
|= pal=(unit pail:neo)
^- (quip card:neo pail:neo)
~& > %mast-init
=/ =pith:neo #/[p/our.bowl]/$/eyre
=/ =binding:eyre [~ /mast]
=/ =req:eyre:neo [%connect binding ~(here moor our.bowl)]
:_ sig/!>(*rig)
:~ [pith %poke eyre-req/!>(req)]
==
::
++ poke
|= [sud=stud:neo vaz=vase]
^- (quip card:neo pail:neo)
:: ~& mast-poke/sud
=+ !<(=rig q.pail)
?+ sud !!
::
%mast-bind :: bind outside of sky
?> =(our.bowl ship.src.bowl)
=+ !<(bind=mast-bind vaz)
=/ =pith:neo #/[p/our.bowl]/$/eyre
=/ =binding:eyre [~ url.bind]
=/ =req:eyre:neo [%connect binding ~(here moor our.bowl)]
=/ =rope (mug view.bind src.bind)
=. endpoints.rig (~(put by endpoints.rig) url.bind [view.bind src.bind])
=? public.rig public.bind
(~(put in public.rig) rope)
:_ sig/!>(rig)
:~ [pith %poke eyre-req/!>(req)]
==
::
%eyre-task :: session creation via http
=+ !<([rid=@ta req=inbound-request:eyre] vaz)
?. authenticated.req [(~(make-auth-redirect res bowl) rid) pail]
?+ method.request.req [(~(make-400 res bowl) rid) pail]
::
%'GET'
=/ url=path (stab url.request.req)
=/ =bind
?: ?=([%mast ^] url)
[i.t.url (pave:neo t.t.url)]
(~(got by endpoints.rig) url)
=/ =rope (mug view.bind src.bind)
=/ =boat ship.src.bowl
?. ?| =(our.bowl boat)
(~(has in public.rig) rope)
==
[(~(make-403 res bowl) rid) pail]
=/ at=pith:neo (~(session moor our.bowl) rope boat)
=/ =made:neo [view.bind ~ (my [%src src.bind] ~)]
=. open-http.rig (~(put by open-http.rig) [rope boat] rid)
:: ~& >> open-http/rid
:_ sig/!>(rig)
:~ [at %cull ~]
[at %make made]
==
::
==
::
%gift
=/ rum=(list [=pith:neo =loot:neo]) ~(tap of:neo !<(gift:neo vaz))
=/ rng ~(. og eny.bowl)
=^ cards rig
=| cards=(list card:neo)
|- ^- (quip card:neo ^rig)
?~ rum
[cards rig]
=/ jig=(unit idea:neo) (~(get of:neo kids.bowl) pith.i.rum)
?~ jig
$(rum t.rum)
=/ =sail (hoist !<(sail q.pail.u.jig))
=/ =rope =/(ud (rear (snip pith.i.rum)) ?>(&(?=(^ ud) ?=(%ud -.ud)) +.ud))
=/ =boat =/(p (rear pith.i.rum) ?>(&(?=(^ p) ?=(%p -.p)) +.p))
=/ rid=(unit @ta) (~(get by open-http.rig) [rope boat])
?^ rid
:: ~& > close-http/u.rid
=^ =buoy rng (rads:rng 1.000.000.000.000)
%= $
open-http.rig (~(del by open-http.rig) [rope boat])
subs.rig (~(put by subs.rig) [rope boat] buoy)
aft.rig (~(put by aft.rig) [rope boat] sail)
cards (weld cards (~(gale res bowl) u.rid rope buoy sail))
rum t.rum
==
?+ mode.loot.i.rum $(rum t.rum)
::
%dif
=/ aft=^sail
=/ sal=(unit ^sail) (~(get by aft.rig) [rope boat])
?^ sal u.sal
(hoist [[%html ~] [[%head ~] ~] [[%body ~] ~] ~])
=/ sub=path (sub-path (~(got by subs.rig) [rope boat]))
%= $
aft.rig (~(put by aft.rig) [rope boat] sail)
cards (weld cards (~(gust res bowl) sub aft sail))
rum t.rum
==
::
==
[cards sig/!>(rig)]
::
%eyre-chan-task
=+ !<(jon=json vaz)
=/ =crow (parse-channel-data jon)
:_ pail
:~ :- (~(session moor our.bowl) rope.crow ship.src.bowl)
[%poke ui-event/!>(`ui-event`[path.crow data.crow])]
==
::
==
::
--
--
::
|%
::
+$ crow :: client to mast channel poke data
[=rope =path data=(map @t @t)]
+$ view @tas :: view imp
+$ bind [=view src=pith:neo] :: view to src binding
+$ rope @ :: view+src bind id
+$ buoy @ :: channel subscription id
+$ boat @p :: src ship session id
+$ sail manx
+$ rig :: :: :: mast state
$: open-http=(map [rope boat] @ta) :: eyre ids pending session creation
endpoints=(map path bind) :: urls to view+src bindings (non-sky)
public=(set rope) :: view+src bindings served beyond =(ship.src our)
subs=(map [rope boat] buoy) :: eyre channel subscription ids by session
aft=(map [rope boat] sail) :: most recent sail state by session
==
::
++ moor :: assumes mast shrub location at /our-ship/mast
|_ our=@p
::
++ here
^- pith:neo
#/[p/our]/mast
::
++ session
|= [=rope =boat]
^- pith:neo
=/ here here
?> &(?=(^ here) ?=(^ t.here))
%_ here
t.t #/[ud/rope]/[p/boat]
==
::
--
::
++ sub-path
|= =buoy
^- path
/eyre-chan/mast/(scot %ud buoy)
::
++ script-node
^- manx
;script: {(trip mast-js)}
::
++ parse-channel-data
|= jon=json
^- crow
((ot ~[rope+ni path+pa data+(om so)]):dejs:format jon)
::
++ res
|_ =bowl:neo
::
++ gale :: :: :: send a full page
|= [rid=@ta =rope =buoy =sail]
^- (list card:neo)
?> ?=(^ c.sail)
%^ make-direct-http-cards
rid
[200 ['Content-Type' 'text/html'] ~]
:- ~
^- octs
%- as-octt:mimes:html
%- en-xml:html
%_ sail
a.g
^- mart
:* [%rope (y-co:co rope)] :: id of bind target for an event poke
[%pith (en-tape:pith:neo ~(here moor our.bowl))] :: destination path, neo to shrub
[%path (spud (sub-path buoy))] :: sub path, shrub to eyre
[%ship +:(scow %p our.bowl)]
[%app "neo"]
a.g.sail
==
c.i.c
(marl [script-node c.i.c.sail])
==
::
++ gust :: :: :: send a diff update
|= [sub=path old=sail new=sail]
^- (list card:neo)
:_ ~
:- #/[p/our.bowl]/$/eyre
:- %poke
:- %eyre-chan-gift
!> ^- chan-gift:eyre:neo
:- sub
^- json
:- %a
%+ algo
?. ?& =(%html n.g.old)
?=(^ c.old) ?=(^ t.c.old)
=(%body n.g.i.t.c.old)
==
[old ~]
[i.t.c.old ~]
?. ?& =(%html n.g.new)
?=(^ c.new) ?=(^ t.c.new)
=(%body n.g.i.t.c.new)
==
[new ~]
[i.t.c.new ~]
::
++ make-400
|= rid=@ta
^- (list card:neo)
%^ make-direct-http-cards
rid
[400 ~]
~
::
++ make-403
|= rid=@ta
^- (list card:neo)
%^ make-direct-http-cards
rid
[403 ~]
~
::
++ make-auth-redirect
|= rid=@ta
^- (list card:neo)
%^ make-direct-http-cards
rid
[307 ['Location' '/~/login?redirect='] ~]
~
::
++ make-direct-http-cards
|= [rid=@ta hed=response-header:http dat=(unit octs)]
^- (list card:neo)
=/ eyre=pith:neo #/[p/our.bowl]/$/eyre
=/ head=sign:eyre:neo [rid %head hed]
=/ data=sign:eyre:neo [rid %data dat]
=/ done=sign:eyre:neo [rid %done ~]
:~ [eyre %poke eyre-sign/!>(head)]
[eyre %poke eyre-sign/!>(data)]
[eyre %poke eyre-sign/!>(done)]
==
::
--
::
++ hoist
|_ =sail
++ $
^- manx
?. ?& =(%html n.g.sail)
?=(^ c.sail) ?=(^ t.c.sail)
=(%body n.g.i.t.c.sail)
==
(anx sail ["" ~])
%_ sail
i.t.c (anx i.t.c.sail ["" ~])
==
++ anx
|= [m=manx key=(pair tape (list @))]
^- manx
=/ fkey=@t (getv %key a.g.m)
=/ nkey=(pair tape (list @)) ?~(fkey key [((w-co:co 1) `@uw`(mug fkey)) ~])
=/ ntap=tape (weld p.nkey ((w-co:co 1) `@uw`(jam q.nkey)))
?: =(%$ n.g.m)
;t-
=key ntap
;+ m
==
%_ m
a.g
^- mart
?~ fkey
[[%key ntap] a.g.m]
a.g.m
c
?: ?| =(%input n.g.m) =(%textarea n.g.m)
=(%script n.g.m) =(%img n.g.m)
=(%link n.g.m) =(%hr n.g.m)
==
c.m
(arl c.m nkey)
==
++ arl
|= [m=marl key=(pair tape (list @))]
=| i=@
|- ^- marl
?~ m ~
:- %+ anx
i.m
key(q [i q.key])
$(m t.m, i +(i))
--
::
++ algo
|= [old=marl new=marl]
=| i=@ud
=| pkey=@t
=| acc=(list json)
|- ^- (list json)
?~ new
?~ old
acc
?: =(%skip- n.g.i.old)
%= $
old t.old
==
:_ acc
^- json
:- %o
%- my
:~ ['p' [%s 'd']]
['q' [%a (turn old |=(m=manx [%s (getv %key a.g.m)]))]]
==
?: =(%$ n.g.i.new)
acc
?: &(?=(^ old) =(%skip- n.g.i.old))
%= $
old t.old
==
?: =(%move- n.g.i.new)
%= $
new t.new
i +(i)
acc
%+ snoc acc
^- json
:- %o
%- my
:~ ['p' [%s 'm']]
['q' [%s (getv %key a.g.i.new)]]
['r' [%n (getv %i a.g.i.new)]]
==
==
=| j=@ud
=/ jold=marl old
=/ nkey=[n=mane k=@t] [n.g.i.new (getv %key a.g.i.new)]
|- ^- (list json)
?~ new
!!
?~ jold
%= ^$
new t.new
i +(i)
acc
%+ snoc acc
^- json
:- %o
%- my
:~ ['p' [%s 'n']]
['q' [%s pkey]]
['r' [%n (scot %ud i)]]
['s' [%s (crip (en-xml:html i.new))]]
==
==
?~ old
!!
?: =(%skip- n.g.i.jold)
%= $
jold t.jold
j +(j)
==
?: =(nkey [n.g.i.jold (getv %key a.g.i.jold)])
?. =(0 j)
=| n=@ud
=/ nnew=marl new
=/ okey=[n=mane k=@t] [n.g.i.old (getv %key a.g.i.old)]
|- ^- (list json)
?~ nnew
%= ^^$
old (snoc t.old i.old)
==
?: =(%move- n.g.i.nnew)
%= $
nnew t.nnew
n +(n)
==
=/ nnky=[n=mane k=@t] [n.g.i.nnew (getv %key a.g.i.nnew)]
?. =(okey nnky)
%= $
nnew t.nnew
n +(n)
==
?: (gte n j)
=/ aupd (upda a.g.i.old a.g.i.nnew)
%= ^^$
old c.i.old
new c.i.nnew
pkey k.nnky
i 0
acc
%= ^^$
old t.old
new
%^ newm new n
;move-(i (scow %ud (add n i)), key (trip k.nnky));
acc
?: &(?=(~ del.aupd) ?=(~ new.aupd))
acc
:_ acc
^- json
:- %o
%- my
:~ ['p' [%s 'c']]
['q' [%s k.nnky]]
['r' [%a del.aupd]]
['s' [%a new.aupd]]
==
==
==
=/ aupd (upda a.g.i.jold a.g.i.new)
%= ^^$
old c.i.jold
new c.i.new
pkey k.nkey
i 0
acc
%= ^^$
old (newm old j ;skip-;)
new t.new
i +(i)
acc
=. acc
%+ snoc acc
^- json
:- %o
%- my
:~ ['p' [%s 'm']]
['q' [%s k.nkey]]
['r' [%n (scot %ud i)]]
==
?: &(?=(~ del.aupd) ?=(~ new.aupd))
acc
:_ acc
^- json
:- %o
%- my
:~ ['p' [%s 'c']]
['q' [%s k.nkey]]
['r' [%a del.aupd]]
['s' [%a new.aupd]]
==
==
==
?: =(%t- n.g.i.new)
?: ?& ?=(^ c.i.old) ?=(^ c.i.new)
?=(^ a.g.i.c.i.old) ?=(^ a.g.i.c.i.new)
=(v.i.a.g.i.c.i.old v.i.a.g.i.c.i.new)
==
%= ^$
old t.old
new t.new
i +(i)
==
=/ txt=@t
?. &(?=(^ c.i.new) ?=(^ a.g.i.c.i.new))
''
(crip v.i.a.g.i.c.i.new)
%= ^$
old t.old
new t.new
i +(i)
acc
:_ acc
^- json
:- %o
%- my
:~ ['p' [%s 't']]
['q' [%s (getv %key a.g.i.new)]]
['r' [%s txt]]
==
==
=/ aupd (upda a.g.i.old a.g.i.new)
%= ^$
old c.i.old
new c.i.new
pkey k.nkey
i 0
acc
%= ^$
old t.old
new t.new
i +(i)
acc
?: &(?=(~ del.aupd) ?=(~ new.aupd))
acc
:_ acc
^- json
:- %o
%- my
:~ ['p' [%s 'c']]
['q' [%s k.nkey]]
['r' [%a del.aupd]]
['s' [%a new.aupd]]
==
==
==
%= $
jold t.jold
j +(j)
==
::
++ getv
|= [t=@tas m=mart]
^- @t
?~ m ''
?: =(n.i.m t)
(crip v.i.m)
$(m t.m)
::
++ upda
|= [om=mart nm=mart]
=| acc=[del=(list json) new=(list json)]
|- ^+ acc
?~ nm
?~ om
acc
%_ acc
del
%+ turn om
|= [n=mane *]
[%s `@t`?>(?=(@ n) n)]
==
=| i=@ud
=/ com=mart om
|- ^+ acc
?~ nm
!!
?~ com
%= ^$
nm t.nm
new.acc
:_ new.acc
:- %a
:~ [%s `@t`?>(?=(@ n.i.nm) n.i.nm)]
[%s (crip v.i.nm)]
==
==
?~ om
!!
?: =(n.i.com n.i.nm)
?: =(v.i.com v.i.nm)
%= ^$
om (oust [i 1] (mart om))
nm t.nm
==
%= ^$
om (oust [i 1] (mart om))
nm t.nm
new.acc
:_ new.acc
:- %a
:~ [%s `@t`?>(?=(@ n.i.nm) n.i.nm)]
[%s (crip v.i.nm)]
==
==
%= $
com t.com
i +(i)
==
::
++ newm
|= [ml=marl i=@ud mx=manx]
=| j=@ud
|- ^- marl
?~ ml
~
:- ?: =(i j)
mx
i.ml
$(ml t.ml, j +(j))
::
--

View File

@ -0,0 +1 @@
json

View File

@ -0,0 +1 @@
manx

View File

@ -0,0 +1,5 @@
$: public=?
url=path
view=@tas
src=pith:neo
==

View File

@ -0,0 +1,3 @@
$: =path
data=(map @t @t)
==

View File

@ -1287,6 +1287,10 @@
==
+$ task
(pair @ta inbound-request:^eyre)
+$ chan-task
json
+$ chan-gift
[sub=path dat=json]
--
:: +clay: Filesystem overlay
::