Page helpers demo: defisland, map-in-children fix, _eval_slot ref evaluator
- Add page-helpers-demo page with defisland ~demo-client-runner (pure SX, zero JS files) showing spec functions running on both server and client - Fix _aser_component children serialization: flatten list results from map instead of serialize(list) which wraps in parens creating ((div ...) ...) that re-parses as invalid function call. Fixed in adapter-async.sx spec and async_eval_ref.py - Switch _eval_slot to use async_eval_ref.py when SX_USE_REF=1 (was hardcoded to async_eval.py) - Add Island type support to async_eval_ref.py: import, SSR rendering, aser dispatch, thread-first, defisland in _ASER_FORMS - Add server affinity check: components with :affinity :server expand even when _expand_components is False - Add diagnostic _aser_stack context to EvalError messages - New spec files: adapter-async.sx, page-helpers.sx, platform_js.py - Bootstrappers: page-helpers module support, performance.now() timing - 0-arity lambda event handler fix in adapter-dom.sx Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2342,452 +2342,147 @@ def env_components(env):
|
||||
return filter(lambda k: (lambda v: (is_component(v) if sx_truthy(is_component(v)) else is_macro(v)))(env_get(env, k)), keys(env))
|
||||
|
||||
|
||||
# === Transpiled from engine (fetch/swap/trigger pure logic) ===
|
||||
# === Transpiled from page-helpers (pure data transformation helpers) ===
|
||||
|
||||
# ENGINE_VERBS
|
||||
ENGINE_VERBS = ['get', 'post', 'put', 'delete', 'patch']
|
||||
# special-form-category-map
|
||||
special_form_category_map = {'if': 'Control Flow', 'when': 'Control Flow', 'cond': 'Control Flow', 'case': 'Control Flow', 'and': 'Control Flow', 'or': 'Control Flow', 'let': 'Binding', 'let*': 'Binding', 'letrec': 'Binding', 'define': 'Binding', 'set!': 'Binding', 'lambda': 'Functions & Components', 'fn': 'Functions & Components', 'defcomp': 'Functions & Components', 'defmacro': 'Functions & Components', 'begin': 'Sequencing & Threading', 'do': 'Sequencing & Threading', '->': 'Sequencing & Threading', 'quote': 'Quoting', 'quasiquote': 'Quoting', 'reset': 'Continuations', 'shift': 'Continuations', 'dynamic-wind': 'Guards', 'map': 'Higher-Order Forms', 'map-indexed': 'Higher-Order Forms', 'filter': 'Higher-Order Forms', 'reduce': 'Higher-Order Forms', 'some': 'Higher-Order Forms', 'every?': 'Higher-Order Forms', 'for-each': 'Higher-Order Forms', 'defstyle': 'Domain Definitions', 'defhandler': 'Domain Definitions', 'defpage': 'Domain Definitions', 'defquery': 'Domain Definitions', 'defaction': 'Domain Definitions'}
|
||||
|
||||
# DEFAULT_SWAP
|
||||
DEFAULT_SWAP = 'outerHTML'
|
||||
# extract-define-kwargs
|
||||
def extract_define_kwargs(expr):
|
||||
result = {}
|
||||
items = slice(expr, 2)
|
||||
n = len(items)
|
||||
for idx in range(0, n):
|
||||
if sx_truthy((((idx + 1) < n) if not sx_truthy(((idx + 1) < n)) else (type_of(nth(items, idx)) == 'keyword'))):
|
||||
key = keyword_name(nth(items, idx))
|
||||
val = nth(items, (idx + 1))
|
||||
result[key] = (sx_str('(', join(' ', map(serialize, val)), ')') if sx_truthy((type_of(val) == 'list')) else sx_str(val))
|
||||
return result
|
||||
|
||||
# parse-time
|
||||
def parse_time(s):
|
||||
if sx_truthy(is_nil(s)):
|
||||
return 0
|
||||
elif sx_truthy(ends_with_p(s, 'ms')):
|
||||
return parse_int(s, 0)
|
||||
elif sx_truthy(ends_with_p(s, 's')):
|
||||
return (parse_int(replace(s, 's', ''), 0) * 1000)
|
||||
# categorize-special-forms
|
||||
def categorize_special_forms(parsed_exprs):
|
||||
categories = {}
|
||||
for expr in parsed_exprs:
|
||||
if sx_truthy(((type_of(expr) == 'list') if not sx_truthy((type_of(expr) == 'list')) else ((len(expr) >= 2) if not sx_truthy((len(expr) >= 2)) else ((type_of(first(expr)) == 'symbol') if not sx_truthy((type_of(first(expr)) == 'symbol')) else (symbol_name(first(expr)) == 'define-special-form'))))):
|
||||
name = nth(expr, 1)
|
||||
kwargs = extract_define_kwargs(expr)
|
||||
category = (get(special_form_category_map, name) if sx_truthy(get(special_form_category_map, name)) else 'Other')
|
||||
if sx_truthy((not sx_truthy(has_key_p(categories, category)))):
|
||||
categories[category] = []
|
||||
get(categories, category).append({'name': name, 'syntax': (get(kwargs, 'syntax') if sx_truthy(get(kwargs, 'syntax')) else ''), 'doc': (get(kwargs, 'doc') if sx_truthy(get(kwargs, 'doc')) else ''), 'tail-position': (get(kwargs, 'tail-position') if sx_truthy(get(kwargs, 'tail-position')) else ''), 'example': (get(kwargs, 'example') if sx_truthy(get(kwargs, 'example')) else '')})
|
||||
return categories
|
||||
|
||||
# build-ref-items-with-href
|
||||
def build_ref_items_with_href(items, base_path, detail_keys, n_fields):
|
||||
return map(lambda item: ((lambda name: (lambda field2: (lambda field3: {'name': name, 'desc': field2, 'exists': field3, 'href': (sx_str(base_path, name) if sx_truthy((field3 if not sx_truthy(field3) else some(lambda k: (k == name), detail_keys))) else NIL)})(nth(item, 2)))(nth(item, 1)))(nth(item, 0)) if sx_truthy((n_fields == 3)) else (lambda name: (lambda desc: {'name': name, 'desc': desc, 'href': (sx_str(base_path, name) if sx_truthy(some(lambda k: (k == name), detail_keys)) else NIL)})(nth(item, 1)))(nth(item, 0))), items)
|
||||
|
||||
# build-reference-data
|
||||
def build_reference_data(slug, raw_data, detail_keys):
|
||||
_match = slug
|
||||
if _match == 'attributes':
|
||||
return {'req-attrs': build_ref_items_with_href(get(raw_data, 'req-attrs'), '/hypermedia/reference/attributes/', detail_keys, 3), 'beh-attrs': build_ref_items_with_href(get(raw_data, 'beh-attrs'), '/hypermedia/reference/attributes/', detail_keys, 3), 'uniq-attrs': build_ref_items_with_href(get(raw_data, 'uniq-attrs'), '/hypermedia/reference/attributes/', detail_keys, 3)}
|
||||
elif _match == 'headers':
|
||||
return {'req-headers': build_ref_items_with_href(get(raw_data, 'req-headers'), '/hypermedia/reference/headers/', detail_keys, 3), 'resp-headers': build_ref_items_with_href(get(raw_data, 'resp-headers'), '/hypermedia/reference/headers/', detail_keys, 3)}
|
||||
elif _match == 'events':
|
||||
return {'events-list': build_ref_items_with_href(get(raw_data, 'events-list'), '/hypermedia/reference/events/', detail_keys, 2)}
|
||||
elif _match == 'js-api':
|
||||
return {'js-api-list': map(lambda item: {'name': nth(item, 0), 'desc': nth(item, 1)}, get(raw_data, 'js-api-list'))}
|
||||
else:
|
||||
return parse_int(s, 0)
|
||||
return {'req-attrs': build_ref_items_with_href(get(raw_data, 'req-attrs'), '/hypermedia/reference/attributes/', detail_keys, 3), 'beh-attrs': build_ref_items_with_href(get(raw_data, 'beh-attrs'), '/hypermedia/reference/attributes/', detail_keys, 3), 'uniq-attrs': build_ref_items_with_href(get(raw_data, 'uniq-attrs'), '/hypermedia/reference/attributes/', detail_keys, 3)}
|
||||
|
||||
# parse-trigger-spec
|
||||
def parse_trigger_spec(spec):
|
||||
if sx_truthy(is_nil(spec)):
|
||||
return NIL
|
||||
# build-attr-detail
|
||||
def build_attr_detail(slug, detail):
|
||||
if sx_truthy(is_nil(detail)):
|
||||
return {'attr-not-found': True}
|
||||
else:
|
||||
raw_parts = split(spec, ',')
|
||||
return filter(lambda x: (not sx_truthy(is_nil(x))), map(lambda part: (lambda tokens: (NIL if sx_truthy(empty_p(tokens)) else ({'event': 'every', 'modifiers': {'interval': parse_time(nth(tokens, 1))}} if sx_truthy(((first(tokens) == 'every') if not sx_truthy((first(tokens) == 'every')) else (len(tokens) >= 2))) else (lambda mods: _sx_begin(for_each(lambda tok: (_sx_dict_set(mods, 'once', True) if sx_truthy((tok == 'once')) else (_sx_dict_set(mods, 'changed', True) if sx_truthy((tok == 'changed')) else (_sx_dict_set(mods, 'delay', parse_time(slice(tok, 6))) if sx_truthy(starts_with_p(tok, 'delay:')) else (_sx_dict_set(mods, 'from', slice(tok, 5)) if sx_truthy(starts_with_p(tok, 'from:')) else NIL)))), rest(tokens)), {'event': first(tokens), 'modifiers': mods}))({}))))(split(trim(part), ' ')), raw_parts))
|
||||
return {'attr-not-found': NIL, 'attr-title': slug, 'attr-description': get(detail, 'description'), 'attr-example': get(detail, 'example'), 'attr-handler': get(detail, 'handler'), 'attr-demo': get(detail, 'demo'), 'attr-wire-id': (sx_str('ref-wire-', replace(replace(slug, ':', '-'), '*', 'star')) if sx_truthy(has_key_p(detail, 'handler')) else NIL)}
|
||||
|
||||
# default-trigger
|
||||
def default_trigger(tag_name):
|
||||
if sx_truthy((tag_name == 'FORM')):
|
||||
return [{'event': 'submit', 'modifiers': {}}]
|
||||
elif sx_truthy(((tag_name == 'INPUT') if sx_truthy((tag_name == 'INPUT')) else ((tag_name == 'SELECT') if sx_truthy((tag_name == 'SELECT')) else (tag_name == 'TEXTAREA')))):
|
||||
return [{'event': 'change', 'modifiers': {}}]
|
||||
# build-header-detail
|
||||
def build_header_detail(slug, detail):
|
||||
if sx_truthy(is_nil(detail)):
|
||||
return {'header-not-found': True}
|
||||
else:
|
||||
return [{'event': 'click', 'modifiers': {}}]
|
||||
return {'header-not-found': NIL, 'header-title': slug, 'header-direction': get(detail, 'direction'), 'header-description': get(detail, 'description'), 'header-example': get(detail, 'example'), 'header-demo': get(detail, 'demo')}
|
||||
|
||||
# get-verb-info
|
||||
def get_verb_info(el):
|
||||
return some(lambda verb: (lambda url: ({'method': upper(verb), 'url': url} if sx_truthy(url) else NIL))(dom_get_attr(el, sx_str('sx-', verb))), ENGINE_VERBS)
|
||||
# build-event-detail
|
||||
def build_event_detail(slug, detail):
|
||||
if sx_truthy(is_nil(detail)):
|
||||
return {'event-not-found': True}
|
||||
else:
|
||||
return {'event-not-found': NIL, 'event-title': slug, 'event-description': get(detail, 'description'), 'event-example': get(detail, 'example'), 'event-demo': get(detail, 'demo')}
|
||||
|
||||
# build-request-headers
|
||||
def build_request_headers(el, loaded_components, css_hash):
|
||||
headers = {'SX-Request': 'true', 'SX-Current-URL': browser_location_href()}
|
||||
target_sel = dom_get_attr(el, 'sx-target')
|
||||
if sx_truthy(target_sel):
|
||||
headers['SX-Target'] = target_sel
|
||||
if sx_truthy((not sx_truthy(empty_p(loaded_components)))):
|
||||
headers['SX-Components'] = join(',', loaded_components)
|
||||
if sx_truthy(css_hash):
|
||||
headers['SX-Css'] = css_hash
|
||||
extra_h = dom_get_attr(el, 'sx-headers')
|
||||
if sx_truthy(extra_h):
|
||||
parsed = parse_header_value(extra_h)
|
||||
if sx_truthy(parsed):
|
||||
for key in keys(parsed):
|
||||
headers[key] = sx_str(get(parsed, key))
|
||||
return headers
|
||||
# build-component-source
|
||||
def build_component_source(comp_data):
|
||||
comp_type = get(comp_data, 'type')
|
||||
name = get(comp_data, 'name')
|
||||
params = get(comp_data, 'params')
|
||||
has_children = get(comp_data, 'has-children')
|
||||
body_sx = get(comp_data, 'body-sx')
|
||||
affinity = get(comp_data, 'affinity')
|
||||
if sx_truthy((comp_type == 'not-found')):
|
||||
return sx_str(';; component ', name, ' not found')
|
||||
else:
|
||||
param_strs = ((['&rest', 'children'] if sx_truthy(has_children) else []) if sx_truthy(empty_p(params)) else (append(cons('&key', params), ['&rest', 'children']) if sx_truthy(has_children) else cons('&key', params)))
|
||||
params_sx = sx_str('(', join(' ', param_strs), ')')
|
||||
form_name = ('defisland' if sx_truthy((comp_type == 'island')) else 'defcomp')
|
||||
affinity_str = (sx_str(' :affinity ', affinity) if sx_truthy(((comp_type == 'component') if not sx_truthy((comp_type == 'component')) else ((not sx_truthy(is_nil(affinity))) if not sx_truthy((not sx_truthy(is_nil(affinity)))) else (not sx_truthy((affinity == 'auto')))))) else '')
|
||||
return sx_str('(', form_name, ' ', name, ' ', params_sx, affinity_str, '\n ', body_sx, ')')
|
||||
|
||||
# process-response-headers
|
||||
def process_response_headers(get_header):
|
||||
return {'redirect': get_header('SX-Redirect'), 'refresh': get_header('SX-Refresh'), 'trigger': get_header('SX-Trigger'), 'retarget': get_header('SX-Retarget'), 'reswap': get_header('SX-Reswap'), 'location': get_header('SX-Location'), 'replace-url': get_header('SX-Replace-Url'), 'css-hash': get_header('SX-Css-Hash'), 'trigger-swap': get_header('SX-Trigger-After-Swap'), 'trigger-settle': get_header('SX-Trigger-After-Settle'), 'content-type': get_header('Content-Type'), 'cache-invalidate': get_header('SX-Cache-Invalidate'), 'cache-update': get_header('SX-Cache-Update')}
|
||||
|
||||
# parse-swap-spec
|
||||
def parse_swap_spec(raw_swap, global_transitions_p):
|
||||
# build-bundle-analysis
|
||||
def build_bundle_analysis(pages_raw, components_raw, total_components, total_macros, pure_count, io_count):
|
||||
_cells = {}
|
||||
parts = split((raw_swap if sx_truthy(raw_swap) else DEFAULT_SWAP), ' ')
|
||||
style = first(parts)
|
||||
_cells['use_transition'] = global_transitions_p
|
||||
for p in rest(parts):
|
||||
if sx_truthy((p == 'transition:true')):
|
||||
_cells['use_transition'] = True
|
||||
elif sx_truthy((p == 'transition:false')):
|
||||
_cells['use_transition'] = False
|
||||
return {'style': style, 'transition': _cells['use_transition']}
|
||||
pages_data = []
|
||||
for page in pages_raw:
|
||||
needed_names = get(page, 'needed-names')
|
||||
n = len(needed_names)
|
||||
pct = (round(((n / total_components) * 100)) if sx_truthy((total_components > 0)) else 0)
|
||||
savings = (100 - pct)
|
||||
_cells['pure_in_page'] = 0
|
||||
_cells['io_in_page'] = 0
|
||||
page_io_refs = []
|
||||
comp_details = []
|
||||
for comp_name in needed_names:
|
||||
info = get(components_raw, comp_name)
|
||||
if sx_truthy((not sx_truthy(is_nil(info)))):
|
||||
if sx_truthy(get(info, 'is-pure')):
|
||||
_cells['pure_in_page'] = (_cells['pure_in_page'] + 1)
|
||||
else:
|
||||
_cells['io_in_page'] = (_cells['io_in_page'] + 1)
|
||||
for ref in (get(info, 'io-refs') if sx_truthy(get(info, 'io-refs')) else []):
|
||||
if sx_truthy((not sx_truthy(some(lambda r: (r == ref), page_io_refs)))):
|
||||
page_io_refs.append(ref)
|
||||
comp_details.append({'name': comp_name, 'is-pure': get(info, 'is-pure'), 'affinity': get(info, 'affinity'), 'render-target': get(info, 'render-target'), 'io-refs': (get(info, 'io-refs') if sx_truthy(get(info, 'io-refs')) else []), 'deps': (get(info, 'deps') if sx_truthy(get(info, 'deps')) else []), 'source': get(info, 'source')})
|
||||
pages_data.append({'name': get(page, 'name'), 'path': get(page, 'path'), 'direct': get(page, 'direct'), 'needed': n, 'pct': pct, 'savings': savings, 'io-refs': len(page_io_refs), 'pure-in-page': _cells['pure_in_page'], 'io-in-page': _cells['io_in_page'], 'components': comp_details})
|
||||
return {'pages': pages_data, 'total-components': total_components, 'total-macros': total_macros, 'pure-count': pure_count, 'io-count': io_count}
|
||||
|
||||
# parse-retry-spec
|
||||
def parse_retry_spec(retry_attr):
|
||||
if sx_truthy(is_nil(retry_attr)):
|
||||
return NIL
|
||||
else:
|
||||
parts = split(retry_attr, ':')
|
||||
return {'strategy': first(parts), 'start-ms': parse_int(nth(parts, 1), 1000), 'cap-ms': parse_int(nth(parts, 2), 30000)}
|
||||
|
||||
# next-retry-ms
|
||||
def next_retry_ms(current_ms, cap_ms):
|
||||
return min((current_ms * 2), cap_ms)
|
||||
|
||||
# filter-params
|
||||
def filter_params(params_spec, all_params):
|
||||
if sx_truthy(is_nil(params_spec)):
|
||||
return all_params
|
||||
elif sx_truthy((params_spec == 'none')):
|
||||
return []
|
||||
elif sx_truthy((params_spec == '*')):
|
||||
return all_params
|
||||
elif sx_truthy(starts_with_p(params_spec, 'not ')):
|
||||
excluded = map(trim, split(slice(params_spec, 4), ','))
|
||||
return filter(lambda p: (not sx_truthy(contains_p(excluded, first(p)))), all_params)
|
||||
else:
|
||||
allowed = map(trim, split(params_spec, ','))
|
||||
return filter(lambda p: contains_p(allowed, first(p)), all_params)
|
||||
|
||||
# resolve-target
|
||||
def resolve_target(el):
|
||||
sel = dom_get_attr(el, 'sx-target')
|
||||
if sx_truthy((is_nil(sel) if sx_truthy(is_nil(sel)) else (sel == 'this'))):
|
||||
return el
|
||||
elif sx_truthy((sel == 'closest')):
|
||||
return dom_parent(el)
|
||||
else:
|
||||
return dom_query(sel)
|
||||
|
||||
# apply-optimistic
|
||||
def apply_optimistic(el):
|
||||
directive = dom_get_attr(el, 'sx-optimistic')
|
||||
if sx_truthy(is_nil(directive)):
|
||||
return NIL
|
||||
else:
|
||||
target = (resolve_target(el) if sx_truthy(resolve_target(el)) else el)
|
||||
state = {'target': target, 'directive': directive}
|
||||
(_sx_begin(_sx_dict_set(state, 'opacity', dom_get_style(target, 'opacity')), dom_set_style(target, 'opacity', '0'), dom_set_style(target, 'pointer-events', 'none')) if sx_truthy((directive == 'remove')) else (_sx_begin(_sx_dict_set(state, 'disabled', dom_get_prop(target, 'disabled')), dom_set_prop(target, 'disabled', True)) if sx_truthy((directive == 'disable')) else ((lambda cls: _sx_begin(_sx_dict_set(state, 'add-class', cls), dom_add_class(target, cls)))(slice(directive, 10)) if sx_truthy(starts_with_p(directive, 'add-class:')) else NIL)))
|
||||
return state
|
||||
|
||||
# revert-optimistic
|
||||
def revert_optimistic(state):
|
||||
if sx_truthy(state):
|
||||
target = get(state, 'target')
|
||||
directive = get(state, 'directive')
|
||||
if sx_truthy((directive == 'remove')):
|
||||
dom_set_style(target, 'opacity', (get(state, 'opacity') if sx_truthy(get(state, 'opacity')) else ''))
|
||||
return dom_set_style(target, 'pointer-events', '')
|
||||
elif sx_truthy((directive == 'disable')):
|
||||
return dom_set_prop(target, 'disabled', (get(state, 'disabled') if sx_truthy(get(state, 'disabled')) else False))
|
||||
elif sx_truthy(get(state, 'add-class')):
|
||||
return dom_remove_class(target, get(state, 'add-class'))
|
||||
return NIL
|
||||
return NIL
|
||||
|
||||
# find-oob-swaps
|
||||
def find_oob_swaps(container):
|
||||
results = []
|
||||
for attr in ['sx-swap-oob', 'hx-swap-oob']:
|
||||
oob_els = dom_query_all(container, sx_str('[', attr, ']'))
|
||||
for oob in oob_els:
|
||||
swap_type = (dom_get_attr(oob, attr) if sx_truthy(dom_get_attr(oob, attr)) else 'outerHTML')
|
||||
target_id = dom_id(oob)
|
||||
dom_remove_attr(oob, attr)
|
||||
if sx_truthy(target_id):
|
||||
results.append({'element': oob, 'swap-type': swap_type, 'target-id': target_id})
|
||||
return results
|
||||
|
||||
# morph-node
|
||||
def morph_node(old_node, new_node):
|
||||
if sx_truthy((dom_has_attr_p(old_node, 'sx-preserve') if sx_truthy(dom_has_attr_p(old_node, 'sx-preserve')) else dom_has_attr_p(old_node, 'sx-ignore'))):
|
||||
return NIL
|
||||
elif sx_truthy((dom_has_attr_p(old_node, 'data-sx-island') if not sx_truthy(dom_has_attr_p(old_node, 'data-sx-island')) else (is_processed_p(old_node, 'island-hydrated') if not sx_truthy(is_processed_p(old_node, 'island-hydrated')) else (dom_has_attr_p(new_node, 'data-sx-island') if not sx_truthy(dom_has_attr_p(new_node, 'data-sx-island')) else (dom_get_attr(old_node, 'data-sx-island') == dom_get_attr(new_node, 'data-sx-island')))))):
|
||||
return morph_island_children(old_node, new_node)
|
||||
elif sx_truthy(((not sx_truthy((dom_node_type(old_node) == dom_node_type(new_node)))) if sx_truthy((not sx_truthy((dom_node_type(old_node) == dom_node_type(new_node))))) else (not sx_truthy((dom_node_name(old_node) == dom_node_name(new_node)))))):
|
||||
return dom_replace_child(dom_parent(old_node), dom_clone(new_node), old_node)
|
||||
elif sx_truthy(((dom_node_type(old_node) == 3) if sx_truthy((dom_node_type(old_node) == 3)) else (dom_node_type(old_node) == 8))):
|
||||
if sx_truthy((not sx_truthy((dom_text_content(old_node) == dom_text_content(new_node))))):
|
||||
return dom_set_text_content(old_node, dom_text_content(new_node))
|
||||
return NIL
|
||||
elif sx_truthy((dom_node_type(old_node) == 1)):
|
||||
sync_attrs(old_node, new_node)
|
||||
if sx_truthy((not sx_truthy((dom_is_active_element_p(old_node) if not sx_truthy(dom_is_active_element_p(old_node)) else dom_is_input_element_p(old_node))))):
|
||||
return morph_children(old_node, new_node)
|
||||
return NIL
|
||||
return NIL
|
||||
|
||||
# sync-attrs
|
||||
def sync_attrs(old_el, new_el):
|
||||
ra_str = (dom_get_attr(old_el, 'data-sx-reactive-attrs') if sx_truthy(dom_get_attr(old_el, 'data-sx-reactive-attrs')) else '')
|
||||
reactive_attrs = ([] if sx_truthy(empty_p(ra_str)) else split(ra_str, ','))
|
||||
for attr in dom_attr_list(new_el):
|
||||
name = first(attr)
|
||||
val = nth(attr, 1)
|
||||
if sx_truthy(((not sx_truthy((dom_get_attr(old_el, name) == val))) if not sx_truthy((not sx_truthy((dom_get_attr(old_el, name) == val)))) else (not sx_truthy(contains_p(reactive_attrs, name))))):
|
||||
dom_set_attr(old_el, name, val)
|
||||
return for_each(lambda attr: (lambda aname: (dom_remove_attr(old_el, aname) if sx_truthy(((not sx_truthy(dom_has_attr_p(new_el, aname))) if not sx_truthy((not sx_truthy(dom_has_attr_p(new_el, aname)))) else ((not sx_truthy(contains_p(reactive_attrs, aname))) if not sx_truthy((not sx_truthy(contains_p(reactive_attrs, aname)))) else (not sx_truthy((aname == 'data-sx-reactive-attrs')))))) else NIL))(first(attr)), dom_attr_list(old_el))
|
||||
|
||||
# morph-children
|
||||
def morph_children(old_parent, new_parent):
|
||||
# build-routing-analysis
|
||||
def build_routing_analysis(pages_raw):
|
||||
_cells = {}
|
||||
old_kids = dom_child_list(old_parent)
|
||||
new_kids = dom_child_list(new_parent)
|
||||
old_by_id = reduce(lambda acc, kid: (lambda id_: (_sx_begin(_sx_dict_set(acc, id_, kid), acc) if sx_truthy(id_) else acc))(dom_id(kid)), {}, old_kids)
|
||||
_cells['oi'] = 0
|
||||
for new_child in new_kids:
|
||||
match_id = dom_id(new_child)
|
||||
match_by_id = (dict_get(old_by_id, match_id) if sx_truthy(match_id) else NIL)
|
||||
if sx_truthy((match_by_id if not sx_truthy(match_by_id) else (not sx_truthy(is_nil(match_by_id))))):
|
||||
if sx_truthy(((_cells['oi'] < len(old_kids)) if not sx_truthy((_cells['oi'] < len(old_kids))) else (not sx_truthy((match_by_id == nth(old_kids, _cells['oi'])))))):
|
||||
dom_insert_before(old_parent, match_by_id, (nth(old_kids, _cells['oi']) if sx_truthy((_cells['oi'] < len(old_kids))) else NIL))
|
||||
morph_node(match_by_id, new_child)
|
||||
_cells['oi'] = (_cells['oi'] + 1)
|
||||
elif sx_truthy((_cells['oi'] < len(old_kids))):
|
||||
old_child = nth(old_kids, _cells['oi'])
|
||||
if sx_truthy((dom_id(old_child) if not sx_truthy(dom_id(old_child)) else (not sx_truthy(match_id)))):
|
||||
dom_insert_before(old_parent, dom_clone(new_child), old_child)
|
||||
else:
|
||||
morph_node(old_child, new_child)
|
||||
_cells['oi'] = (_cells['oi'] + 1)
|
||||
pages_data = []
|
||||
_cells['client_count'] = 0
|
||||
_cells['server_count'] = 0
|
||||
for page in pages_raw:
|
||||
has_data = get(page, 'has-data')
|
||||
content_src = (get(page, 'content-src') if sx_truthy(get(page, 'content-src')) else '')
|
||||
_cells['mode'] = NIL
|
||||
_cells['reason'] = ''
|
||||
if sx_truthy(has_data):
|
||||
_cells['mode'] = 'server'
|
||||
_cells['reason'] = 'Has :data expression — needs server IO'
|
||||
_cells['server_count'] = (_cells['server_count'] + 1)
|
||||
elif sx_truthy(empty_p(content_src)):
|
||||
_cells['mode'] = 'server'
|
||||
_cells['reason'] = 'No content expression'
|
||||
_cells['server_count'] = (_cells['server_count'] + 1)
|
||||
else:
|
||||
dom_append(old_parent, dom_clone(new_child))
|
||||
return for_each(lambda i: ((lambda leftover: (dom_remove_child(old_parent, leftover) if sx_truthy((dom_is_child_of_p(leftover, old_parent) if not sx_truthy(dom_is_child_of_p(leftover, old_parent)) else ((not sx_truthy(dom_has_attr_p(leftover, 'sx-preserve'))) if not sx_truthy((not sx_truthy(dom_has_attr_p(leftover, 'sx-preserve')))) else (not sx_truthy(dom_has_attr_p(leftover, 'sx-ignore')))))) else NIL))(nth(old_kids, i)) if sx_truthy((i >= _cells['oi'])) else NIL), range(_cells['oi'], len(old_kids)))
|
||||
_cells['mode'] = 'client'
|
||||
_cells['client_count'] = (_cells['client_count'] + 1)
|
||||
pages_data.append({'name': get(page, 'name'), 'path': get(page, 'path'), 'mode': _cells['mode'], 'has-data': has_data, 'content-expr': (sx_str(slice(content_src, 0, 80), '...') if sx_truthy((len(content_src) > 80)) else content_src), 'reason': _cells['reason']})
|
||||
return {'pages': pages_data, 'total-pages': (_cells['client_count'] + _cells['server_count']), 'client-count': _cells['client_count'], 'server-count': _cells['server_count']}
|
||||
|
||||
# morph-island-children
|
||||
def morph_island_children(old_island, new_island):
|
||||
old_lakes = dom_query_all(old_island, '[data-sx-lake]')
|
||||
new_lakes = dom_query_all(new_island, '[data-sx-lake]')
|
||||
old_marshes = dom_query_all(old_island, '[data-sx-marsh]')
|
||||
new_marshes = dom_query_all(new_island, '[data-sx-marsh]')
|
||||
new_lake_map = {}
|
||||
new_marsh_map = {}
|
||||
for lake in new_lakes:
|
||||
id_ = dom_get_attr(lake, 'data-sx-lake')
|
||||
if sx_truthy(id_):
|
||||
new_lake_map[id_] = lake
|
||||
for marsh in new_marshes:
|
||||
id_ = dom_get_attr(marsh, 'data-sx-marsh')
|
||||
if sx_truthy(id_):
|
||||
new_marsh_map[id_] = marsh
|
||||
for old_lake in old_lakes:
|
||||
id_ = dom_get_attr(old_lake, 'data-sx-lake')
|
||||
new_lake = dict_get(new_lake_map, id_)
|
||||
if sx_truthy(new_lake):
|
||||
sync_attrs(old_lake, new_lake)
|
||||
morph_children(old_lake, new_lake)
|
||||
for old_marsh in old_marshes:
|
||||
id_ = dom_get_attr(old_marsh, 'data-sx-marsh')
|
||||
new_marsh = dict_get(new_marsh_map, id_)
|
||||
if sx_truthy(new_marsh):
|
||||
morph_marsh(old_marsh, new_marsh, old_island)
|
||||
return process_signal_updates(new_island)
|
||||
|
||||
# morph-marsh
|
||||
def morph_marsh(old_marsh, new_marsh, island_el):
|
||||
transform = dom_get_data(old_marsh, 'sx-marsh-transform')
|
||||
env = dom_get_data(old_marsh, 'sx-marsh-env')
|
||||
new_html = dom_inner_html(new_marsh)
|
||||
if sx_truthy((env if not sx_truthy(env) else (new_html if not sx_truthy(new_html) else (not sx_truthy(empty_p(new_html)))))):
|
||||
parsed = parse(new_html)
|
||||
sx_content = (invoke(transform, parsed) if sx_truthy(transform) else parsed)
|
||||
dispose_marsh_scope(old_marsh)
|
||||
return with_marsh_scope(old_marsh, lambda : (lambda new_dom: _sx_begin(dom_remove_children_after(old_marsh, NIL), dom_append(old_marsh, new_dom)))(render_to_dom(sx_content, env, NIL)))
|
||||
else:
|
||||
sync_attrs(old_marsh, new_marsh)
|
||||
return morph_children(old_marsh, new_marsh)
|
||||
|
||||
# process-signal-updates
|
||||
def process_signal_updates(root):
|
||||
signal_els = dom_query_all(root, '[data-sx-signal]')
|
||||
return for_each(lambda el: (lambda spec: ((lambda colon_idx: ((lambda store_name: (lambda raw_value: _sx_begin((lambda parsed: reset_b(use_store(store_name), parsed))(json_parse(raw_value)), dom_remove_attr(el, 'data-sx-signal')))(slice(spec, (colon_idx + 1))))(slice(spec, 0, colon_idx)) if sx_truthy((colon_idx > 0)) else NIL))(index_of(spec, ':')) if sx_truthy(spec) else NIL))(dom_get_attr(el, 'data-sx-signal')), signal_els)
|
||||
|
||||
# swap-dom-nodes
|
||||
def swap_dom_nodes(target, new_nodes, strategy):
|
||||
_match = strategy
|
||||
if _match == 'innerHTML':
|
||||
if sx_truthy(dom_is_fragment_p(new_nodes)):
|
||||
return morph_children(target, new_nodes)
|
||||
else:
|
||||
wrapper = dom_create_element('div', NIL)
|
||||
dom_append(wrapper, new_nodes)
|
||||
return morph_children(target, wrapper)
|
||||
elif _match == 'outerHTML':
|
||||
parent = dom_parent(target)
|
||||
((lambda fc: (_sx_begin(morph_node(target, fc), (lambda sib: insert_remaining_siblings(parent, target, sib))(dom_next_sibling(fc))) if sx_truthy(fc) else dom_remove_child(parent, target)))(dom_first_child(new_nodes)) if sx_truthy(dom_is_fragment_p(new_nodes)) else morph_node(target, new_nodes))
|
||||
return parent
|
||||
elif _match == 'afterend':
|
||||
return dom_insert_after(target, new_nodes)
|
||||
elif _match == 'beforeend':
|
||||
return dom_append(target, new_nodes)
|
||||
elif _match == 'afterbegin':
|
||||
return dom_prepend(target, new_nodes)
|
||||
elif _match == 'beforebegin':
|
||||
return dom_insert_before(dom_parent(target), new_nodes, target)
|
||||
elif _match == 'delete':
|
||||
return dom_remove_child(dom_parent(target), target)
|
||||
elif _match == 'none':
|
||||
return NIL
|
||||
else:
|
||||
if sx_truthy(dom_is_fragment_p(new_nodes)):
|
||||
return morph_children(target, new_nodes)
|
||||
else:
|
||||
wrapper = dom_create_element('div', NIL)
|
||||
dom_append(wrapper, new_nodes)
|
||||
return morph_children(target, wrapper)
|
||||
|
||||
# insert-remaining-siblings
|
||||
def insert_remaining_siblings(parent, ref_node, sib):
|
||||
if sx_truthy(sib):
|
||||
next = dom_next_sibling(sib)
|
||||
dom_insert_after(ref_node, sib)
|
||||
return insert_remaining_siblings(parent, sib, next)
|
||||
return NIL
|
||||
|
||||
# swap-html-string
|
||||
def swap_html_string(target, html, strategy):
|
||||
_match = strategy
|
||||
if _match == 'innerHTML':
|
||||
return dom_set_inner_html(target, html)
|
||||
elif _match == 'outerHTML':
|
||||
parent = dom_parent(target)
|
||||
dom_insert_adjacent_html(target, 'afterend', html)
|
||||
dom_remove_child(parent, target)
|
||||
return parent
|
||||
elif _match == 'afterend':
|
||||
return dom_insert_adjacent_html(target, 'afterend', html)
|
||||
elif _match == 'beforeend':
|
||||
return dom_insert_adjacent_html(target, 'beforeend', html)
|
||||
elif _match == 'afterbegin':
|
||||
return dom_insert_adjacent_html(target, 'afterbegin', html)
|
||||
elif _match == 'beforebegin':
|
||||
return dom_insert_adjacent_html(target, 'beforebegin', html)
|
||||
elif _match == 'delete':
|
||||
return dom_remove_child(dom_parent(target), target)
|
||||
elif _match == 'none':
|
||||
return NIL
|
||||
else:
|
||||
return dom_set_inner_html(target, html)
|
||||
|
||||
# handle-history
|
||||
def handle_history(el, url, resp_headers):
|
||||
push_url = dom_get_attr(el, 'sx-push-url')
|
||||
replace_url = dom_get_attr(el, 'sx-replace-url')
|
||||
hdr_replace = get(resp_headers, 'replace-url')
|
||||
if sx_truthy(hdr_replace):
|
||||
return browser_replace_state(hdr_replace)
|
||||
elif sx_truthy((push_url if not sx_truthy(push_url) else (not sx_truthy((push_url == 'false'))))):
|
||||
return browser_push_state((url if sx_truthy((push_url == 'true')) else push_url))
|
||||
elif sx_truthy((replace_url if not sx_truthy(replace_url) else (not sx_truthy((replace_url == 'false'))))):
|
||||
return browser_replace_state((url if sx_truthy((replace_url == 'true')) else replace_url))
|
||||
return NIL
|
||||
|
||||
# PRELOAD_TTL
|
||||
PRELOAD_TTL = 30000
|
||||
|
||||
# preload-cache-get
|
||||
def preload_cache_get(cache, url):
|
||||
entry = dict_get(cache, url)
|
||||
if sx_truthy(is_nil(entry)):
|
||||
return NIL
|
||||
else:
|
||||
if sx_truthy(((now_ms() - get(entry, 'timestamp')) > PRELOAD_TTL)):
|
||||
dict_delete(cache, url)
|
||||
return NIL
|
||||
else:
|
||||
dict_delete(cache, url)
|
||||
return entry
|
||||
|
||||
# preload-cache-set
|
||||
def preload_cache_set(cache, url, text, content_type):
|
||||
return _sx_dict_set(cache, url, {'text': text, 'content-type': content_type, 'timestamp': now_ms()})
|
||||
|
||||
# classify-trigger
|
||||
def classify_trigger(trigger):
|
||||
event = get(trigger, 'event')
|
||||
if sx_truthy((event == 'every')):
|
||||
return 'poll'
|
||||
elif sx_truthy((event == 'intersect')):
|
||||
return 'intersect'
|
||||
elif sx_truthy((event == 'load')):
|
||||
return 'load'
|
||||
elif sx_truthy((event == 'revealed')):
|
||||
return 'revealed'
|
||||
else:
|
||||
return 'event'
|
||||
|
||||
# should-boost-link?
|
||||
def should_boost_link_p(link):
|
||||
href = dom_get_attr(link, 'href')
|
||||
return (href if not sx_truthy(href) else ((not sx_truthy(starts_with_p(href, '#'))) if not sx_truthy((not sx_truthy(starts_with_p(href, '#')))) else ((not sx_truthy(starts_with_p(href, 'javascript:'))) if not sx_truthy((not sx_truthy(starts_with_p(href, 'javascript:')))) else ((not sx_truthy(starts_with_p(href, 'mailto:'))) if not sx_truthy((not sx_truthy(starts_with_p(href, 'mailto:')))) else (browser_same_origin_p(href) if not sx_truthy(browser_same_origin_p(href)) else ((not sx_truthy(dom_has_attr_p(link, 'sx-get'))) if not sx_truthy((not sx_truthy(dom_has_attr_p(link, 'sx-get')))) else ((not sx_truthy(dom_has_attr_p(link, 'sx-post'))) if not sx_truthy((not sx_truthy(dom_has_attr_p(link, 'sx-post')))) else (not sx_truthy(dom_has_attr_p(link, 'sx-disable'))))))))))
|
||||
|
||||
# should-boost-form?
|
||||
def should_boost_form_p(form):
|
||||
return ((not sx_truthy(dom_has_attr_p(form, 'sx-get'))) if not sx_truthy((not sx_truthy(dom_has_attr_p(form, 'sx-get')))) else ((not sx_truthy(dom_has_attr_p(form, 'sx-post'))) if not sx_truthy((not sx_truthy(dom_has_attr_p(form, 'sx-post')))) else (not sx_truthy(dom_has_attr_p(form, 'sx-disable')))))
|
||||
|
||||
# parse-sse-swap
|
||||
def parse_sse_swap(el):
|
||||
return (dom_get_attr(el, 'sx-sse-swap') if sx_truthy(dom_get_attr(el, 'sx-sse-swap')) else 'message')
|
||||
|
||||
|
||||
# === Transpiled from router (client-side route matching) ===
|
||||
|
||||
# split-path-segments
|
||||
def split_path_segments(path):
|
||||
trimmed = (slice(path, 1) if sx_truthy(starts_with_p(path, '/')) else path)
|
||||
trimmed2 = (slice(trimmed, 0, (len(trimmed) - 1)) if sx_truthy(((not sx_truthy(empty_p(trimmed))) if not sx_truthy((not sx_truthy(empty_p(trimmed)))) else ends_with_p(trimmed, '/'))) else trimmed)
|
||||
if sx_truthy(empty_p(trimmed2)):
|
||||
return []
|
||||
else:
|
||||
return split(trimmed2, '/')
|
||||
|
||||
# make-route-segment
|
||||
def make_route_segment(seg):
|
||||
if sx_truthy((starts_with_p(seg, '<') if not sx_truthy(starts_with_p(seg, '<')) else ends_with_p(seg, '>'))):
|
||||
param_name = slice(seg, 1, (len(seg) - 1))
|
||||
d = {}
|
||||
d['type'] = 'param'
|
||||
d['value'] = param_name
|
||||
return d
|
||||
else:
|
||||
d = {}
|
||||
d['type'] = 'literal'
|
||||
d['value'] = seg
|
||||
return d
|
||||
|
||||
# parse-route-pattern
|
||||
def parse_route_pattern(pattern):
|
||||
segments = split_path_segments(pattern)
|
||||
return map(make_route_segment, segments)
|
||||
|
||||
# match-route-segments
|
||||
def match_route_segments(path_segs, parsed_segs):
|
||||
_cells = {}
|
||||
if sx_truthy((not sx_truthy((len(path_segs) == len(parsed_segs))))):
|
||||
return NIL
|
||||
else:
|
||||
params = {}
|
||||
_cells['matched'] = True
|
||||
for_each_indexed(lambda i, parsed_seg: ((lambda path_seg: (lambda seg_type: ((_sx_cell_set(_cells, 'matched', False) if sx_truthy((not sx_truthy((path_seg == get(parsed_seg, 'value'))))) else NIL) if sx_truthy((seg_type == 'literal')) else (_sx_dict_set(params, get(parsed_seg, 'value'), path_seg) if sx_truthy((seg_type == 'param')) else _sx_cell_set(_cells, 'matched', False))))(get(parsed_seg, 'type')))(nth(path_segs, i)) if sx_truthy(_cells['matched']) else NIL), parsed_segs)
|
||||
if sx_truthy(_cells['matched']):
|
||||
return params
|
||||
else:
|
||||
return NIL
|
||||
|
||||
# match-route
|
||||
def match_route(path, pattern):
|
||||
path_segs = split_path_segments(path)
|
||||
parsed_segs = parse_route_pattern(pattern)
|
||||
return match_route_segments(path_segs, parsed_segs)
|
||||
|
||||
# find-matching-route
|
||||
def find_matching_route(path, routes):
|
||||
_cells = {}
|
||||
path_segs = split_path_segments(path)
|
||||
_cells['result'] = NIL
|
||||
for route in routes:
|
||||
if sx_truthy(is_nil(_cells['result'])):
|
||||
params = match_route_segments(path_segs, get(route, 'parsed'))
|
||||
if sx_truthy((not sx_truthy(is_nil(params)))):
|
||||
matched = merge(route, {})
|
||||
matched['params'] = params
|
||||
_cells['result'] = matched
|
||||
return _cells['result']
|
||||
# build-affinity-analysis
|
||||
def build_affinity_analysis(demo_components, page_plans):
|
||||
return {'components': demo_components, 'page-plans': page_plans}
|
||||
|
||||
|
||||
# === Transpiled from signals (reactive signal runtime) ===
|
||||
@@ -3058,4 +2753,4 @@ def render(expr, env=None):
|
||||
|
||||
def make_env(**kwargs):
|
||||
"""Create an environment with initial bindings."""
|
||||
return _Env(dict(kwargs))
|
||||
return _Env(dict(kwargs))
|
||||
Reference in New Issue
Block a user