browser-module-runtime 0.0.0-stage → 0.0.2

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.
@@ -0,0 +1,40 @@
1
+ function isNodeLike() {
2
+ return typeof process !== "undefined" && !!process.versions?.node;
3
+ }
4
+
5
+ export function createDefaultModuleUrlBackend() {
6
+ if (!isNodeLike() && typeof Blob !== "undefined" && typeof URL?.createObjectURL === "function") {
7
+ return createBlobModuleUrlBackend();
8
+ }
9
+
10
+ return createDataModuleUrlBackend();
11
+ }
12
+
13
+ export function createBlobModuleUrlBackend() {
14
+ return {
15
+ kind: "blob",
16
+ create(code) {
17
+ return URL.createObjectURL(new Blob([code], { type: "text/javascript" }));
18
+ },
19
+ revoke(url) {
20
+ URL.revokeObjectURL(url);
21
+ },
22
+ };
23
+ }
24
+
25
+ export function createDataModuleUrlBackend() {
26
+ let sequence = 0;
27
+
28
+ return {
29
+ kind: "data",
30
+ create(code) {
31
+ // Native ESM caches modules by URL. Recreating the same data: URL after
32
+ // invalidation would otherwise return the previously evaluated module.
33
+ // Give every backend create() call a fresh module identity, matching the
34
+ // naturally unique identity of URL.createObjectURL() in the blob backend.
35
+ const identity = ++sequence;
36
+ return `data:text/javascript;charset=utf-8,${encodeURIComponent(code)}#browser-module-runtime-${identity}`;
37
+ },
38
+ revoke() {},
39
+ };
40
+ }
@@ -0,0 +1 @@
1
+ export * from '../../html.js';
@@ -0,0 +1,74 @@
1
+ /** HTML controller with opt-in observation; not coupled to any UI framework. */
2
+ import {registerWebComponent} from '../web-components/index.js';
3
+
4
+ const registries=new WeakMap();
5
+
6
+ function parseSchema(value,context){
7
+ if(!value)return undefined;
8
+ try {const parsed=JSON.parse(value);if(!parsed||typeof parsed!=='object'||Array.isArray(parsed))throw new Error('expected an object');return parsed;}
9
+ catch(error){throw new Error(`Invalid prop schema in ${context}: ${error.message}`);}
10
+ }
11
+
12
+ export function registerHTML(runtime,{name='default',root=globalThis.document,observe=false}={}) {
13
+ if(!root)throw new TypeError('registerHTML requires a DOM root');
14
+ const doc=root.ownerDocument||root;
15
+ let registry=registries.get(doc);
16
+ if(!registry){registry=new Map();registries.set(doc,registry);}
17
+ if(registry.has(name))throw new Error(`Runtime HTML name already registered: ${name}`);
18
+ registry.set(name,runtime);
19
+ const active=new Map();
20
+ let observer;
21
+ let alive=true;
22
+ let pending=Promise.resolve();
23
+ function defineElement(tag,config){return registerWebComponent(runtime,{...config,tag,document:doc});}
24
+ function elementOptions(el){
25
+ return {
26
+ module:el.getAttribute('module'),
27
+ export:el.getAttribute('component')||'default',
28
+ props:parseSchema(el.getAttribute('element-props')||el.getAttribute('props'),el.tagName.toLowerCase()),
29
+ shadow:el.getAttribute('shadow')||'none'
30
+ };
31
+ }
32
+ function isOwned(node) {return node.getAttribute('data-runtime')===name || node.getAttribute('runtime')===name || (!node.hasAttribute('data-runtime')&&!node.hasAttribute('runtime')&&name==='default');}
33
+ async function scan(){
34
+ if(!alive)return;
35
+ for(const el of root.querySelectorAll('script[type][module]')){
36
+ if(!isOwned(el)||el.dataset.runtimeRegistered==='true')continue;
37
+ const type=el.getAttribute('type');
38
+ if(!runtime.getSupportedTypes().includes(type))continue;
39
+ const id=el.getAttribute('module');
40
+ if(el.hasAttribute('data-src'))await runtime.loadSource(id,{type,url:el.getAttribute('data-src')});
41
+ else runtime.define(id,runtime.normalizeInlineSource(el.textContent,{type,id,element:el}),{type});
42
+ if(el.hasAttribute('custom-element')){
43
+ defineElement(el.getAttribute('custom-element'),elementOptions(el));
44
+ }
45
+ el.dataset.runtimeRegistered='true';
46
+ }
47
+ for(const el of root.querySelectorAll('runtime-element[tag][module]')){
48
+ if(!isOwned(el)||el.dataset.runtimeRegistered==='true')continue;
49
+ defineElement(el.getAttribute('tag'),elementOptions(el));
50
+ el.dataset.runtimeRegistered='true';
51
+ el.hidden=true;
52
+ }
53
+ const targets=root.querySelectorAll('runtime-render');
54
+ for(const el of targets){
55
+ if(!isOwned(el)||active.has(el))continue;
56
+ let props={};
57
+ try {props=JSON.parse(el.getAttribute('props')||'{}');}catch(e){throw new Error(`Invalid props JSON in runtime-render: ${e.message}`);}
58
+ if(!props||typeof props!=='object'||Array.isArray(props))throw new Error('runtime-render props must be a JSON object');
59
+ const module=el.getAttribute('module');if(!module)continue;
60
+ const token={cancelled:false,handle:null};active.set(el,token);
61
+ try {token.handle=await runtime.mount({module,target:el,props,export:el.getAttribute('component')||'default'});if(token.cancelled)await token.handle.dispose();}
62
+ catch(e){active.delete(el);el.dispatchEvent(new CustomEvent('runtime-error',{detail:e}));throw e;}
63
+ }
64
+ for(const [el,token] of active){if(!el.isConnected||!root.contains(el)){active.delete(el);token.cancelled=true;await token.handle?.dispose();}}
65
+ }
66
+ function scheduleScan(){pending=pending.catch(()=>{}).then(scan);return pending;}
67
+ if(observe){observer=new MutationObserver(()=>scheduleScan());observer.observe(root,{subtree:true,childList:true});}
68
+ // Explicitly register this custom element only once per browser window.
69
+ const win=doc.defaultView;
70
+ if(win?.customElements&&!win.customElements.get('runtime-render')){
71
+ win.customElements.define('runtime-render',class extends win.HTMLElement {});
72
+ }
73
+ return {name,runtime,defineElement,registerExisting:scheduleScan,scan:scheduleScan,async disconnect(){if(!alive)return;alive=false;observer?.disconnect();await pending.catch(()=>{});for(const token of active.values()){token.cancelled=true;await token.handle?.dispose();}active.clear();registry.delete(name);}};
74
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from '../index.js';
package/src/index.js ADDED
@@ -0,0 +1,99 @@
1
+ import {createRuntime as createModuleEngine} from './engine/runtime.js';
2
+ import {createBuildPipeline} from './build/pipeline.js';
3
+ import {createStyleManager} from './resources/styles.js';
4
+ import {mergePluginPackageEntries} from './plugins/package-entries.js';
5
+
6
+ /** Framework-neutral, synchronous factory. Async plugin setup is available on `.ready`. */
7
+ export function createRuntime(options={}) {
8
+ const plugins=options.plugins||[];
9
+ const artifactById=new Map();
10
+ const metadata=new Map();
11
+ const pipeline=createBuildPipeline({plugins,defaultType:options.defaultType||'javascript',onArtifact:(id,artifact)=>artifactById.set(id,artifact)});
12
+ const effectivePackages=mergePluginPackageEntries(options.packages,plugins);
13
+ const core=createModuleEngine({...options,packages:effectivePackages,compiler:pipeline});
14
+ const styles=createStyleManager();
15
+ const mounts=new Set();
16
+ let disposed=false;
17
+ const runtime={
18
+ ...core,
19
+ ready:null,
20
+ define(id,source,config={}) {
21
+ const type=pipeline.detectType(id,config.type||config.format);
22
+ metadata.set(id,{type});
23
+ return core.define(id,source,{...config,format:type});
24
+ },
25
+ defineMany(definitions) {
26
+ return definitions.map(({id,source,...opts})=>runtime.define(id,source,opts));
27
+ },
28
+ update(id,source,config={}) {
29
+ const type=pipeline.detectType(id,config.type||config.format||metadata.get(id)?.type);
30
+ metadata.set(id,{type});
31
+ return core.update(id,source,{...config,format:type});
32
+ },
33
+ remove(id,opts){metadata.delete(id);artifactById.delete(id);return core.remove(id,opts);},
34
+ clear(opts){const ids=core.clear(opts);for(const id of ids){metadata.delete(id);artifactById.delete(id);}return ids;},
35
+ getModuleInfo(id){const info=core.getModuleInfo(id);return info?{...info,type:metadata.get(id)?.type}:info;},
36
+ getBuildArtifact(id){return artifactById.get(id);},
37
+ getSourceType(id){return metadata.get(id)?.type;},
38
+ getSupportedTypes(){return [...pipeline.formats];},
39
+ normalizeInlineSource(source,{type,id,element}={}) {
40
+ const owner=pipeline.owners.get(type);
41
+ const plugin=plugins.find(p=>p.name===owner?.plugin);
42
+ const normalize=plugin?.html?.normalizeInlineSource;
43
+ return normalize ? normalize(source,{type,id,element,runtime}) : source;
44
+ },
45
+ async loadSource(id,{type,url=id}={}) {
46
+ await runtime.ready;
47
+ let source;
48
+ for(const plugin of plugins)if(plugin.load){source=await plugin.load({id,url,type}, {runtime});if(source!=null)break;}
49
+ if(source==null) {
50
+ const response=await fetch(url);
51
+ if(!response.ok)throw new Error(`Failed loading ${url}: HTTP ${response.status}`);
52
+ source=await response.text();
53
+ }
54
+ runtime.define(id, typeof source==='string'?source:source.code,{type});
55
+ return id;
56
+ },
57
+ async import(id){await runtime.ready;return core.import(id);},
58
+ async compile(id,opts){await runtime.ready;return core.compile(id,opts);},
59
+ async mount({module:id,export:exportName='default',target,props={}}) {
60
+ await runtime.ready;
61
+ if(typeof target==='string')target=document.querySelector(target);
62
+ if(!target)throw new TypeError(`Mount target not found: ${String(target)}`);
63
+ const type=metadata.get(id)?.type;
64
+ if(!type)throw new Error(`Module ${id} has no registered source type; define it before mounting.`);
65
+ const owner=pipeline.owners.get(type);
66
+ const adapter=plugins.find(p=>p.name===owner?.plugin);
67
+ const renderer=adapter?.renderers?.find(r=>(r.types||[]).includes(type));
68
+ if(!renderer)throw new Error(`No renderer registered for ${type}`);
69
+ const built=await runtime.compile(id);
70
+ const ns=await runtime.import(id);
71
+ if(!(exportName in ns))throw new Error(`Module ${id} does not export ${exportName}`);
72
+ const releases=[];
73
+ let handle;
74
+ try {
75
+ for(const asset of built.assets||[]) {
76
+ if(asset.type==='css')releases.push(styles.acquire({id:asset.id||`${id}:css`,content:asset.content},target.getRootNode?.()||target.ownerDocument));
77
+ }
78
+ handle=await renderer.mount({component:ns[exportName],target,props,runtime,id});
79
+ }catch(error){releases.forEach(fn=>fn());throw error;}
80
+ let closed=false;
81
+ const out={ready:Promise.resolve(),capabilities:{updateProps:typeof handle?.updateProps==='function'},
82
+ async updateProps(next){if(closed)throw new Error('Mount is disposed');if(typeof handle?.updateProps!=='function')throw new Error(`Renderer ${renderer.name||adapter.name} does not support updateProps`);return handle.updateProps(next);},
83
+ async dispose(){if(closed)return;closed=true;mounts.delete(out);try{await handle?.dispose?.();}finally{releases.forEach(fn=>fn());}}
84
+ };
85
+ mounts.add(out);
86
+ return out;
87
+ },
88
+ async dispose(){if(disposed)return;disposed=true;await Promise.allSettled([...mounts].map(m=>m.dispose()));styles.dispose();core.dispose();for(const plugin of [...plugins].reverse())await plugin.dispose?.();}
89
+ };
90
+ runtime.ready=Promise.resolve().then(async()=>{for(const plugin of plugins)await plugin.configure?.({runtime,pipeline});});
91
+ return runtime;
92
+ }
93
+
94
+ export {analyzeImports} from './build/analyze.js';
95
+ export {BuildPhaseError,createBuildPipeline} from './build/pipeline.js';
96
+ export {createStyleManager} from './resources/styles.js';
97
+ export {createPackagePolicy,PackageVersionResolutionError,parsePackageSpecifier} from './engine/package-resolution/index.js';
98
+ export {createMemoryCompileCache,createLocalStorageCompileCache,createIndexedDBCompileCache} from './engine/compile-cache.js';
99
+ export {createDefaultModuleUrlBackend,createBlobModuleUrlBackend,createDataModuleUrlBackend} from './engine/url-backend.js';
@@ -0,0 +1,30 @@
1
+ /** Merge framework-owned package entries into the ordinary per-runtime policy.
2
+ * This is link-time configuration, not a browser import-map mutation. */
3
+ import {createPackagePolicy,PackageVersionResolutionError} from '../engine/package-resolution/index.js';
4
+
5
+ export function mergePluginPackageEntries(packages,plugins=[]) {
6
+ const source=packages??{};
7
+ const versions={...(source.versions??{})};
8
+ const entries={...(source.entries??{})};
9
+ for(const plugin of plugins) {
10
+ for(const [name,descriptor] of Object.entries(plugin.packageEntries??{})) {
11
+ const pluginPolicy=createPackagePolicy({entries:{[name]:descriptor}});
12
+ const provided=pluginPolicy.inspect(name);
13
+ if(Object.hasOwn(versions,name)||Object.hasOwn(entries,name)) {
14
+ const userPolicy=createPackagePolicy({
15
+ provider:source.provider,
16
+ ...(Object.hasOwn(versions,name)?{versions:{[name]:versions[name]}}:{entries:{[name]:entries[name]}}),
17
+ });
18
+ const selected=userPolicy.inspect(name);
19
+ if(selected?.url!==provided.url) {
20
+ throw new PackageVersionResolutionError('PACKAGE_VERSION_CONFLICT',
21
+ `Plugin ${JSON.stringify(plugin.name)} requires ${JSON.stringify(name)} from ${JSON.stringify(provided.url)}, but the runtime specifies ${JSON.stringify(selected?.url)}. Align the versions/providers instead of loading two framework runtimes.`,
22
+ {packageName:name,plugin:plugin.name,expected:provided.url,actual:selected?.url});
23
+ }
24
+ continue;
25
+ }
26
+ entries[name]=descriptor;
27
+ }
28
+ }
29
+ return {...source,versions,entries};
30
+ }
@@ -0,0 +1,18 @@
1
+ /** Per-resource-target stylesheet refcounts. Styles survive until final release. */
2
+ export function createStyleManager() {
3
+ const sheets = new Map();
4
+ function acquire({id,content},target = document) {
5
+ if(!target?.createElement && !target?.ownerDocument)throw new TypeError('A DOM document or shadow root is required for styles.');
6
+ const doc = target.ownerDocument || target;
7
+ const root = target.head || target;
8
+ let byTarget=sheets.get(root);if(!byTarget){byTarget=new Map();sheets.set(root,byTarget);}
9
+ const key=String(id);let entry=byTarget.get(key);
10
+ if(entry && entry.content!==content)throw new Error(`CSS asset conflict: ${key}`);
11
+ if(!entry){const element=doc.createElement('style');element.setAttribute('data-runtime-style',key);element.textContent=content;root.append(element);entry={element,count:0,content};byTarget.set(key,entry);}
12
+ entry.count++;
13
+ let released=false;
14
+ return ()=>{if(released)return;released=true;if(--entry.count===0){entry.element.remove();byTarget.delete(key);if(!byTarget.size)sheets.delete(root);}};
15
+ }
16
+ function dispose(){for(const records of sheets.values())for(const entry of records.values())entry.element.remove();sheets.clear();}
17
+ return { acquire,dispose,get size(){return [...sheets.values()].reduce((n,group)=>n+group.size,0);} };
18
+ }
@@ -0,0 +1,11 @@
1
+ import type {Runtime} from '../../index.js';
2
+ export type PropType='string'|'number'|'boolean'|'json';
3
+ export type PropSchema=Record<string,PropType|{type:PropType;attribute?:string;default?:unknown}>;
4
+ export interface WebComponentOptions {
5
+ tag:string;module:string;export?:string;props?:PropSchema;
6
+ /** Only light DOM is supported at this stage. */
7
+ shadow?:'none'|false;
8
+ document?:Document;
9
+ }
10
+ /** Register a native custom element backed by the shared runtime mounting API. */
11
+ export declare function registerWebComponent(runtime:Runtime,options:WebComponentOptions):CustomElementConstructor;
@@ -0,0 +1,145 @@
1
+ /** Native custom elements for any browser-module-runtime renderer. No framework imports. */
2
+ const validName=/^[a-z][a-z0-9._-]*-[a-z0-9._-]+$/;
3
+ const types=new Set(['string','number','boolean','json']);
4
+
5
+ function normalizeSchema(input) {
6
+ if(input==null)return {};
7
+ if(typeof input!=='object'||Array.isArray(input))throw new TypeError('Web component props must be a schema object');
8
+ const schema={};
9
+ const attrs=new Set();
10
+ for(const [key,spec] of Object.entries(input)){
11
+ const config=typeof spec==='string'?{type:spec}:spec;
12
+ if(!config||typeof config!=='object'||!types.has(config.type))throw new TypeError(`Invalid prop type for ${key}; expected string, number, boolean, or json`);
13
+ const attribute=config.attribute||key.replace(/[A-Z]/g,c=>'-'+c.toLowerCase());
14
+ if(!/^[a-z][a-z0-9._-]*$/.test(attribute)||attrs.has(attribute))throw new TypeError(`Invalid or duplicate attribute: ${attribute}`);
15
+ attrs.add(attribute);
16
+ schema[key]={...config,attribute};
17
+ }
18
+ return schema;
19
+ }
20
+ function convertAttribute(value,spec,key) {
21
+ if(value===null)return spec.default!==undefined?spec.default:spec.type==='boolean'?false:undefined;
22
+ if(spec.type==='string')return value;
23
+ if(spec.type==='boolean')return true; // standard HTML boolean attribute presence semantics
24
+ if(spec.type==='number'){
25
+ if(value.trim()===''||!Number.isFinite(Number(value)))throw new TypeError(`Invalid number for attribute ${spec.attribute}: ${value}`);
26
+ return Number(value);
27
+ }
28
+ try{return JSON.parse(value);}catch(e){throw new TypeError(`Invalid JSON for attribute ${spec.attribute}: ${e.message}`);}
29
+ }
30
+ const inferAttribute=value=>{
31
+ if(value==='')return true;
32
+ if(value==='true')return true;
33
+ if(value==='false')return false;
34
+ if(/^[-+]?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?$/i.test(value)&&Number.isFinite(Number(value)))return Number(value);
35
+ if(/^[\[{]/.test(value))try{return JSON.parse(value);}catch{}
36
+ return value;
37
+ };
38
+ function dispatch(element,name,detail){element.dispatchEvent(new (element.ownerDocument.defaultView.CustomEvent)(name,{detail,bubbles:true,composed:true}));}
39
+
40
+ /** Register a framework-rendered runtime module as a native custom element.
41
+ * v1 mounts in light DOM. Without renderer.updateProps, changes trigger a remount.
42
+ */
43
+ export function registerWebComponent(runtime,options={}) {
44
+ const {tag,module,export:exportName='default',props:propsOption,shadow='none',document:doc=globalThis.document}=options;
45
+ if(!runtime||typeof runtime.mount!=='function')throw new TypeError('registerWebComponent requires a runtime with mount()');
46
+ if(typeof tag!=='string'||!validName.test(tag))throw new TypeError(`Invalid custom element name: ${String(tag)} (must contain a hyphen)`);
47
+ if(typeof module!=='string'||!module)throw new TypeError('registerWebComponent requires a module id');
48
+ if(shadow!=='none'&&shadow!==false)throw new Error('Shadow DOM is not supported yet; use shadow: "none" for light DOM');
49
+ const win=doc?.defaultView;
50
+ if(!win?.customElements)throw new TypeError('registerWebComponent requires a browser document with customElements');
51
+ if(win.customElements.get(tag))throw new Error(`Custom element already registered: ${tag}`);
52
+ const schema=normalizeSchema(propsOption);
53
+ const attributes=Object.values(schema).map(s=>s.attribute);
54
+ class RuntimeElement extends win.HTMLElement {
55
+ static get observedAttributes(){return attributes;}
56
+ constructor(){
57
+ super();
58
+ this._runtimeHandle=null;
59
+ this._mounting=null;
60
+ this._generation=0;
61
+ this._propertyValues=Object.create(null);
62
+ this._updateTask=Promise.resolve();
63
+ // Properties assigned before customElements.define() must survive upgrade.
64
+ this._preUpgrade=[];
65
+ for(const key of Object.keys(schema))if(Object.hasOwn(this,key)){
66
+ this._preUpgrade.push([key,this[key]]);delete this[key];
67
+ }
68
+ }
69
+ _collectProps(){
70
+ const props={};
71
+ if(!attributes.length){
72
+ for(const attr of this.attributes)props[attr.name]=inferAttribute(attr.value);
73
+ }
74
+ for(const [key,spec] of Object.entries(schema)){
75
+ const value=convertAttribute(this.getAttribute(spec.attribute),spec,key);
76
+ if(value!==undefined)props[key]=value;
77
+ }
78
+ Object.assign(props,this._propertyValues);
79
+ return props;
80
+ }
81
+ _report(error){dispatch(this,'runtime-error',{error,module,tag});}
82
+ connectedCallback(){
83
+ for(const [key,value] of this._preUpgrade.splice(0))this[key]=value;
84
+ const version=++this._generation;
85
+ queueMicrotask(()=>{if(this.isConnected&&version===this._generation)this._start(version);});
86
+ }
87
+ disconnectedCallback(){
88
+ const version=++this._generation;
89
+ queueMicrotask(()=>{
90
+ if(!this.isConnected&&version===this._generation)this._stop().catch(e=>this._report(e));
91
+ });
92
+ }
93
+ attributeChangedCallback(attr,oldValue,newValue){
94
+ if(oldValue===newValue)return;
95
+ const entry=Object.entries(schema).find(([,spec])=>spec.attribute===attr);
96
+ if(entry)delete this._propertyValues[entry[0]];
97
+ if(this.isConnected)this._scheduleUpdate();
98
+ }
99
+ _scheduleUpdate(){
100
+ this._updateTask=this._updateTask.catch(()=>{}).then(async()=>{
101
+ // Allow batched changes and a mount in progress to settle before applying changes.
102
+ if(this._mounting)await this._mounting;
103
+ if(!this.isConnected||!this._runtimeHandle)return;
104
+ const props=this._collectProps();
105
+ if(this._runtimeHandle.capabilities.updateProps){
106
+ await this._runtimeHandle.updateProps(props);
107
+ }else{
108
+ // An explicit remount is preferable to silently ignoring changed attributes.
109
+ await this._runtimeHandle.dispose();this._runtimeHandle=null;
110
+ await this._start(this._generation);
111
+ }
112
+ }).catch(e=>this._report(e));
113
+ }
114
+ async _start(version){
115
+ if(this._runtimeHandle||this._mounting||!this.isConnected||version!==this._generation)return;
116
+ try{
117
+ const props=this._collectProps();
118
+ const mountPromise=runtime.mount({module,export:exportName,target:this,props});
119
+ this._mounting=mountPromise;
120
+ const handle=await mountPromise;
121
+ if(!this.isConnected||version!==this._generation){await handle.dispose();return;}
122
+ this._runtimeHandle=handle;
123
+ dispatch(this,'runtime-ready',{module,tag,handle});
124
+ }catch(e){this._report(e);}finally{
125
+ this._mounting=null;
126
+ if(this.isConnected&&!this._runtimeHandle&&version!==this._generation)
127
+ queueMicrotask(()=>this._start(this._generation));
128
+ }
129
+ }
130
+ async _stop(){
131
+ // A pending mount observes generation changes and disposes itself.
132
+ const handle=this._runtimeHandle;this._runtimeHandle=null;
133
+ if(handle){await handle.dispose();dispatch(this,'runtime-disposed',{module,tag});}
134
+ }
135
+ }
136
+ for(const key of Object.keys(schema)){
137
+ Object.defineProperty(RuntimeElement.prototype,key,{
138
+ configurable:true,enumerable:true,
139
+ get(){return Object.hasOwn(this._propertyValues,key)?this._propertyValues[key]:convertAttribute(this.getAttribute(schema[key].attribute),schema[key],key);},
140
+ set(value){this._propertyValues[key]=value;if(this.isConnected)this._scheduleUpdate();}
141
+ });
142
+ }
143
+ win.customElements.define(tag,RuntimeElement);
144
+ return RuntimeElement;
145
+ }
@@ -0,0 +1 @@
1
+ export * from './src/web-components/index.js';