crann 1.0.9 → 1.0.19

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/dist/cjs/index.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var k=Object.create;var l=Object.defineProperty;var w=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var R=Object.getPrototypeOf,E=Object.prototype.hasOwnProperty;var U=(a,t)=>{for(var e in t)l(a,e,{get:t[e],enumerable:!0})},h=(a,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of A(t))!E.call(a,n)&&n!==e&&l(a,n,{get:()=>t[n],enumerable:!(i=w(t,n))||i.enumerable});return a};var O=(a,t,e)=>(e=a!=null?k(R(a)):{},h(t||!a||!a.__esModule?l(e,"default",{value:a,enumerable:!0}):e,a)),j=a=>h(l({},"__esModule",{value:!0}),a);var M={};U(M,{Crann:()=>C,Partition:()=>m,Persistence:()=>y,connect:()=>b,create:()=>I});module.exports=j(M);var d=O(require("webextension-polyfill"));var m={Instance:"instance",Common:"common"},y={Session:"session",Local:"local",None:"none"};var D=require("porter-source");function S(a,t){if(a===t)return!0;if(a==null||typeof a!="object"||t==null||typeof t!="object")return!1;let e=Object.keys(a),i=Object.keys(t);if(e.length!==i.length)return!1;e.sort(),i.sort();for(let n=0;n<e.length;n++){let o=e[n];if(o!==i[n]||!S(a[o],t[o]))return!1}return!0}var C=class{constructor(t,e){this.config=t;this.instances=new Map;this.stateChangeListeners=[];this.storagePrefix="crann_";this.post=()=>{};console.log("Crann constructor"),this.defaultInstanceState=this.initializeInstanceDefault(),this.defaultCommonState=this.commonState=this.initializeCommonDefault(),this.hydrate();let[i,n,o,s]=(0,D.source)("crann");this.post=i,o(({key:r,connectionType:g,context:p,location:u})=>{console.log("Crann porter connect",r,g,p,u),this.addInstance(r),s(({key:v,connectionType:c,context:f,location:T})=>{console.log("Crann porter connect",v,c,f,T),this.removeInstance(v)})}),this.storagePrefix=e??this.storagePrefix}async addInstance(t){if(this.instances.has(t))console.warn("Crann instance already exists",t);else{let e={...this.defaultInstanceState};this.instances.set(t,e)}}async removeInstance(t){this.instances.has(t)?this.instances.delete(t):console.warn("Crann instance does not exist",t)}async setCommonState(t){let e={...this.commonState,...t};S(this.commonState,e)||(this.commonState=e,await this.persist(t),this.notify(t))}async setInstanceState(t,e){let i=this.instances.get(t)||this.defaultInstanceState,n={...i,...e};S(i,n)||(this.instances.set(t,n),this.notify(e,t))}async persist(t){for(let e in t||this.commonState){let n=this.config[e].persist||"none",o=t?t[e]:this.commonState[e];switch(n){case"session":await d.default.storage.session.set({[this.storagePrefix+e]:o});break;case"local":await d.default.storage.local.set({[this.storagePrefix+e]:o});break;default:break}}}async clear(){this.commonState=this.defaultCommonState,this.instances.forEach((t,e)=>{this.instances.set(e,this.defaultInstanceState)}),await this.persist(),this.notify({})}subscribe(t){this.stateChangeListeners.push(t)}notify(t,e){let i=e?this.get(e):this.get();this.stateChangeListeners.forEach(n=>n(i,t,e)),e?this.post({action:"stateUpdate",payload:{state:t}},e):this.instances.forEach((n,o)=>{this.post({action:"stateUpdate",payload:{state:t}},o)})}get(t){return t?{...this.commonState,...this.instances.get(t)}:{...this.commonState}}async set(t,e){let i={},n={};for(let o in t){let s=this.config[o];if(s.partition==="instance"){let r=o,g=t;i[r]=g[r]}else if(!s.partition||s.partition===m.Common){let r=o,g=t;n[r]=g[r]}}e&&this.setInstanceState(e,i),this.setCommonState(n)}async hydrate(){let t=await d.default.storage.local.get(null),e=await d.default.storage.session.get(null),i={...t,...e},n={};for(let o in i){let s=this.removePrefix(o);if(this.config.hasOwnProperty(s)){let r=i[s];n[s]=r}}this.commonState={...this.defaultCommonState,...n}}removePrefix(t){return t.startsWith(this.storagePrefix)?t.replace(this.storagePrefix,""):t}initializeInstanceDefault(){let t={};return Object.keys(this.config).forEach(e=>{let i=this.config[e];i.partition==="instance"&&(t[e]=i.default)}),t}initializeCommonDefault(){let t={};return Object.keys(this.config).forEach(e=>{let i=this.config[e];i.partition===m.Common&&(t[e]=i.default)}),t}};function I(a,t){return new C(a,t)}var P=require("porter-source");function b(a,t){let[e,i]=(0,P.connect)({namespace:"crann"}),n=L(a),o=null,s=new Map,r=0;return i({stateUpdate:c=>{o=c.payload.state,n={...n,...o},o&&s.forEach(f=>{(f.keys===void 0||f.keys.some(x=>o.hasOwnProperty(x)))&&f.callback(o)})}}),{get:()=>n,set:c=>{e({action:"setState",payload:{state:c}})},subscribe:(c,f)=>(s.set(++r,{keys:f,callback:c}),r),unsubscribe:c=>{s.delete(c)}}}function L(a){let t={};Object.keys(a).forEach(i=>{let n=a[i];n.partition==="instance"&&(t[i]=n.default)});let e={};return Object.keys(a).forEach(i=>{let n=a[i];n.partition==="instance"&&(e[i]=n.default)}),{...t,...e}}0&&(module.exports={Crann,Partition,Persistence,connect,create});
1
+ "use strict";var M=Object.create;var T=Object.defineProperty;var E=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var O=Object.getPrototypeOf,N=Object.prototype.hasOwnProperty;var q=(a,t)=>{for(var e in t)T(a,e,{get:t[e],enumerable:!0})},A=(a,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of j(t))!N.call(a,i)&&i!==e&&T(a,i,{get:()=>t[i],enumerable:!(n=E(t,i))||n.enumerable});return a};var z=(a,t,e)=>(e=a!=null?M(O(a)):{},A(t||!a||!a.__esModule?T(e,"default",{value:a,enumerable:!0}):e,a)),F=a=>A(T({},"__esModule",{value:!0}),a);var H={};q(H,{Crann:()=>p,Partition:()=>S,Persistence:()=>w,connect:()=>R,connected:()=>U,create:()=>L});module.exports=F(H);var v=z(require("webextension-polyfill"));var S={Instance:"instance",Common:"common"},w={Session:"session",Local:"local",None:"none"};var C=require("porter-source");function D(a,t){if(a===t)return!0;if(a==null||typeof a!="object"||t==null||typeof t!="object")return!1;let e=Object.keys(a),n=Object.keys(t);if(e.length!==n.length)return!1;e.sort(),n.sort();for(let i=0;i<e.length;i++){let o=e[i];if(o!==n[i]||!D(a[o],t[o]))return!1}return!0}var m=class m{constructor(t,e){this.config=t;this.instances=new Map;this.stateChangeListeners=[];this.storagePrefix="crann_";this.post=()=>{};this.log("Constructing"),this.defaultInstanceState=this.initializeInstanceDefault(),this.defaultCommonState=this.commonState=this.initializeCommonDefault(),this.hydrate(),this.log("Initializing porter");let[n,i,o,s,c]=(0,C.source)("crann");this.post=n,i({setState:(r,l)=>{if(!l){this.log("setState message heard from unknown agent");return}this.instanceLog("Setting state: ",l.key,r),this.set(r.payload.state,l.key)}}),c(r=>{this.instanceLog("Messages set received. Sending initial state.",r.key,r);let l=this.get(r.key);this.post({action:"initialState",payload:{state:l,key:r.key}},r.key)}),o(({key:r,connectionType:l,context:y,location:P})=>{this.instanceLog("Agent connected. Connection type, context and location: ",r,l,y,P),this.addInstance(r),s(({key:d,connectionType:x,context:g,location:f})=>{this.instanceLog("Agent disconnect heard. Connection type, context and location: ",d,x,g,f),this.removeInstance(d)})}),this.storagePrefix=e??this.storagePrefix}static getInstance(t,e){return m.instance?console.log("CrannSource [static-core], Instance requested and already existed, returning"):m.instance=new m(t,e),m.instance}async addInstance(t){if(this.instances.has(t))this.instanceLog("Instance was already registered, ignoring request from key: ",t);else{this.instanceLog("Adding instance from agent key: ",t);let e={...this.defaultInstanceState};this.instances.set(t,e)}}async removeInstance(t){this.instances.has(t)?(this.instanceLog("Remove instance requested. ",t),this.instances.delete(t)):this.instanceLog("Remove instance requested but it did not exist!. ",t)}async setCommonState(t){this.log("Request to set common state with: ",t);let e={...this.commonState,...t};D(this.commonState,e)?this.log("New state seems to be the same as existing, skipping"):(this.log("Confirmed new state was different than existing so proceeding to persist then notify all connected instances."),this.commonState=e,await this.persist(t),this.notify(t))}async setInstanceState(t,e){this.instanceLog("Request to update instance state, update: ",t,e);let n=this.instances.get(t)||this.defaultInstanceState,i={...n,...e};D(n,i)?this.instanceLog("Instance state update is not different, skipping update. ",t):(this.instanceLog("Instance state update is different, updating and notifying. ",t),this.instances.set(t,i),this.notify(e,t))}async persist(t){this.log("Persisting state");let e=!1;for(let n in t||this.commonState){let o=this.config[n].persist||"none",s=t?t[n]:this.commonState[n];switch(o){case"session":await v.default.storage.session.set({[this.storagePrefix+n]:s}),e=!0;break;case"local":await v.default.storage.local.set({[this.storagePrefix+n]:s}),e=!0;break;default:break}}e?this.log("State was persisted"):this.log("Nothing to persist")}async clear(){this.log("Clearing state"),this.commonState=this.defaultCommonState,this.instances.forEach((t,e)=>{this.instances.set(e,this.defaultInstanceState)}),await this.persist(),this.notify({})}subscribe(t){this.log("Subscribing to state"),this.stateChangeListeners.push(t)}notify(t,e){let n=e?(0,C.getMetadata)(e):void 0,i=n?(0,C.getTarget)(n):void 0,o=e?this.get(e):this.get();this.stateChangeListeners.length>0&&(this.log("Notifying state change listeners in source"),this.stateChangeListeners.forEach(s=>{s(o,t,n||void 0)})),e&&i?(this.instanceLog("Notifying of state change.",e),this.post({action:"stateUpdate",payload:{state:t}},i)):(console.log("Notifying everyone"),this.instances.forEach((s,c)=>{this.post({action:"stateUpdate",payload:{state:t}},c)}))}get(t){return t?{...this.commonState,...this.instances.get(t)}:{...this.commonState}}findInstance(t,e){let n=(0,C.getKey)({context:t,...e});for(let[i,o]of this.instances)if(i===n)return this.log("Found instance for key: ",i),i;return this.log("Could not find instance for context and location: ",t,e),null}async set(t,e){let n={},i={};for(let o in t){let s=this.config[o];if(s.partition==="instance"){let c=o,r=t;n[c]=r[c]}else if(!s.partition||s.partition===S.Common){let c=o,r=t;i[c]=r[c]}}e&&Object.keys(n).length>0&&(this.instanceLog("Setting instance state: ",e,n),this.setInstanceState(e,n)),Object.keys(i).length>0&&(this.log("Setting common state: ",i),this.setCommonState(i))}async hydrate(){this.log("Hydrating state from storage.");let t=await v.default.storage.local.get(null),e=await v.default.storage.session.get(null),n={...t,...e},i={},o=!1;for(let s in n){let c=this.removePrefix(s);if(this.config.hasOwnProperty(c)){let r=n[c];i[c]=r,o=!0}}o?this.log("Hydrated some items."):this.log("No items found in storage."),this.commonState={...this.defaultCommonState,...i}}removePrefix(t){return t.startsWith(this.storagePrefix)?t.replace(this.storagePrefix,""):t}initializeInstanceDefault(){let t={};return Object.keys(this.config).forEach(e=>{let n=this.config[e];n.partition==="instance"&&(t[e]=n.default)}),t}initializeCommonDefault(){let t={};return Object.keys(this.config).forEach(e=>{let n=this.config[e];n.partition===S.Common&&(t[e]=n.default)}),t}log(t,...e){console.log("CrannSource [core], "+t,...e)}instanceLog(t,e,...n){console.log(`CrannSource [${e}], `+t,...n)}error(t,...e){console.error("CrannSource [core], "+t,...e)}warn(t,...e){console.warn("CrannSource [core], "+t,...e)}};m.instance=null;var p=m;function L(a,t){let e=p.getInstance(a,t);return[e.get.bind(e),e.set.bind(e),e.subscribe.bind(e),e.findInstance.bind(e)]}var K=require("porter-source"),h=null;function R(a,t){let e="unset";if(d("Initializing with context: ",t),h)return d("We had an instance already, returning"),h;d("No existing instance, creating a new one");let[n,i]=(0,K.connect)({namespace:"crann",agentContext:t});i({initialState:g=>{o=g.payload.state,e=g.payload.key,c.forEach(f=>{f.callback(o)}),d(`Initial state received and ${c.size} listeners notified`)},stateUpdate:g=>{d("State updated: ",g),s=g.payload.state,o={...o,...s},s&&(d("Notifying listeners of state update"),c.forEach(f=>{f.keys===void 0?(d("Found a universal listener, notifying"),f.callback(s)):f.keys.some(I=>s.hasOwnProperty(I))&&(d("Found a specific listener for this item, notifying"),f.callback(s))}))}}),d("Porter connected. Setting up state and listeners");let o=$(a),s=null,c=new Set;d("Completed setup, returning instance");let r=()=>o,l=g=>{console.log("CrannAgent, calling post with setState"),n({action:"setState",payload:{state:g}})},y=(g,f)=>{let u={keys:f,callback:g};return c.add(u),()=>{c.delete(u)}},P=g=>{let f=()=>r()[g],u=b=>l({[g]:b}),I=b=>y(k=>{g in k&&b(k[g])},[g]);return[f(),u,I]};function d(g,...f){console.log(`CrannAgent [${e}] `+g,...f)}return h=[P,r,l,y],h}function U(){return h!==null}function $(a){let t={};Object.keys(a).forEach(n=>{let i=a[n];i.partition==="instance"&&(t[n]=i.default)});let e={};return Object.keys(a).forEach(n=>{let i=a[n];i.partition==="instance"&&(e[n]=i.default)}),{...t,...e}}0&&(module.exports={Crann,Partition,Persistence,connect,connected,create});
2
2
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts", "../../src/crann.ts", "../../src/model/crann.model.ts", "../../src/utils/deepEqual.ts", "../../src/crannAgent.ts"],
4
- "sourcesContent": ["export { create, Crann } from './crann';\nexport { connect } from './crannAgent';\nexport { CrannAgent, StateUpdate, State, Partition, Persistence } from './model/crann.model';", "import browser from 'webextension-polyfill';\nimport { DerivedInstanceState, DerivedCommonState, ConfigItem, DerivedState, Partition, CrannAgent, StateSubscriber } from './model/crann.model';\nimport { source, } from 'porter-source';\nimport { deepEqual } from './utils/deepEqual';\nimport { AgentLocation, AgentMetadata } from 'porter-source/dist/types/porter.model';\n\nexport class Crann<TConfig extends Record<string, ConfigItem<any>>> {\n private instances: Map<string, DerivedInstanceState<TConfig>> = new Map();\n private defaultCommonState: DerivedCommonState<TConfig>;\n private defaultInstanceState: DerivedInstanceState<TConfig>;\n private commonState: DerivedCommonState<TConfig>;\n private stateChangeListeners: Array<(state: (DerivedInstanceState<TConfig> | DerivedState<TConfig>), changes: Partial<DerivedCommonState<TConfig> & DerivedInstanceState<TConfig>>, key?: string) => void> = [];\n private storagePrefix = 'crann_';\n private post: (message: any, contextOrKey: string, location?: Partial<AgentLocation>) => void = () => { };\n\n constructor(private config: TConfig, storagePrefix?: string) {\n console.log('Crann constructor');\n this.defaultInstanceState = this.initializeInstanceDefault();\n this.defaultCommonState = this.commonState = this.initializeCommonDefault();\n this.hydrate();\n const [post, _setMessages, onConnect, onDisconnect] = source('crann');\n this.post = post;\n onConnect(({ key, connectionType, context, location }) => {\n console.log('Crann porter connect', key, connectionType, context, location);\n this.addInstance(key);\n onDisconnect(({ key, connectionType, context, location }) => {\n console.log('Crann porter connect', key, connectionType, context, location);\n this.removeInstance(key);\n });\n });\n this.storagePrefix = storagePrefix ?? this.storagePrefix;\n }\n\n private async addInstance(key: string): Promise<void> {\n if (!this.instances.has(key)) {\n const initialInstanceState = { ...this.defaultInstanceState } as DerivedInstanceState<TConfig>;\n this.instances.set(key, initialInstanceState);\n } else {\n console.warn('Crann instance already exists', key);\n }\n }\n\n private async removeInstance(key: string): Promise<void> {\n if (this.instances.has(key)) {\n this.instances.delete(key);\n } else {\n console.warn('Crann instance does not exist', key);\n }\n }\n\n public async setCommonState(state: Partial<DerivedCommonState<TConfig>>): Promise<void> {\n const update = { ...this.commonState, ...state };\n if (!deepEqual(this.commonState, update)) {\n this.commonState = update;\n await this.persist(state);\n this.notify(state as Partial<DerivedState<TConfig>>);\n }\n }\n\n public async setInstanceState(key: string, state: Partial<DerivedInstanceState<TConfig>>): Promise<void> {\n const currentState = this.instances.get(key) || this.defaultInstanceState;\n const update = { ...currentState, ...state };\n if (!deepEqual(currentState, update)) {\n this.instances.set(key, update);\n this.notify(state as Partial<DerivedState<TConfig>>, key);\n }\n }\n\n // If we pass in specific state to persist, it only persists that state. \n // Otherwise persists all of the worker state.\n private async persist(state?: Partial<DerivedCommonState<TConfig>>): Promise<void> {\n for (const key in (state || this.commonState)) {\n const item = this.config[key] as ConfigItem<any>;\n const persistence = item.persist || 'none';\n const value = state ? state[key as keyof DerivedCommonState<TConfig>] : this.commonState[key];\n switch (persistence) {\n case 'session':\n await browser.storage.session.set({ [this.storagePrefix + (key as string)]: value });\n break;\n case 'local':\n await browser.storage.local.set({ [this.storagePrefix + (key as string)]: value });\n break;\n default:\n break;\n }\n }\n }\n\n public async clear(): Promise<void> {\n this.commonState = this.defaultCommonState;\n this.instances.forEach((_, key) => {\n this.instances.set(key, this.defaultInstanceState);\n });\n await this.persist();\n this.notify({});\n }\n\n public subscribe(listener: (state: (DerivedInstanceState<TConfig> | DerivedState<TConfig>), changes: Partial<DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>>, key?: string) => void): void {\n this.stateChangeListeners.push(listener);\n }\n\n private notify(changes: Partial<DerivedState<TConfig>>, key?: string): void {\n const state = key ? this.get(key) : this.get();\n this.stateChangeListeners.forEach(listener => listener(state, changes, key));\n if (key) {\n this.post({ action: 'stateUpdate', payload: { state: changes } }, key);\n } else {\n // for every key of this.instances, post the state update to the corresponding key\n this.instances.forEach((_, key) => {\n this.post({ action: 'stateUpdate', payload: { state: changes } }, key);\n });\n }\n }\n\n public get(): DerivedState<TConfig>;\n public get(key: string): DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>;\n public get(key?: string): (DerivedCommonState<TConfig> | DerivedState<TConfig>) {\n if (!key) {\n return { ...this.commonState, ...{} as DerivedInstanceState<TConfig> };\n }\n return { ...this.commonState, ...this.instances.get(key) };\n }\n\n public async set(state: Partial<DerivedCommonState<TConfig>>): Promise<void>\n public async set(state: Partial<DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>>, key: string): Promise<void>\n public async set(state: Partial<DerivedInstanceState<TConfig> | DerivedCommonState<TConfig>>, key?: string): Promise<void> {\n const instance = {} as Partial<DerivedInstanceState<TConfig>>;\n const worker = {} as Partial<DerivedCommonState<TConfig>>;\n\n for (const itemKey in state) {\n const item = this.config[itemKey as keyof TConfig] as ConfigItem<any>;\n if (item.partition === 'instance') {\n const instanceItemKey = itemKey as keyof DerivedInstanceState<TConfig>;\n const instanceState = state as Partial<DerivedInstanceState<TConfig>>;\n instance[instanceItemKey] = instanceState[instanceItemKey];\n } else if (!item.partition || item.partition === Partition.Common) {\n const commonItemKey = itemKey as keyof DerivedCommonState<TConfig>;\n const commonState = state as Partial<DerivedCommonState<TConfig>>;\n worker[commonItemKey] = commonState[commonItemKey]!;\n }\n }\n if (key) this.setInstanceState(key, instance);\n this.setCommonState(worker);\n }\n\n private async hydrate(): Promise<void> {\n const local = await browser.storage.local.get(null);\n const session = await browser.storage.session.get(null);\n const combined = { ...local, ...session };\n const update: Partial<DerivedCommonState<TConfig>> = {}; // Cast update as Partial<DerivedState<TConfig>>\n for (const prefixedKey in combined) {\n const key = this.removePrefix(prefixedKey);\n if (this.config.hasOwnProperty(key)) {\n const value = combined[key];\n update[key as keyof DerivedCommonState<TConfig>] = value;\n }\n }\n this.commonState = { ...this.defaultCommonState, ...update };\n }\n\n private removePrefix(key: string): string {\n if (key.startsWith(this.storagePrefix)) {\n return key.replace(this.storagePrefix, '');\n }\n return key;\n }\n\n private initializeInstanceDefault(): DerivedInstanceState<TConfig> {\n const instanceState: any = {};\n Object.keys(this.config).forEach(key => {\n const item: ConfigItem<any> = this.config[key];\n if (item.partition === 'instance') {\n instanceState[key] = item.default;\n }\n });\n return instanceState;\n }\n\n private initializeCommonDefault(): DerivedCommonState<TConfig> {\n const commonState: any = {};\n Object.keys(this.config).forEach(key => {\n const item: ConfigItem<any> = this.config[key];\n if (item.partition === Partition.Common) {\n commonState[key] = item.default;\n }\n });\n return commonState;\n }\n}\n\n\nexport function create<TConfig extends Record<string, ConfigItem<any>>>(config: TConfig, storagePrefix?: string): Crann<TConfig> {\n return new Crann(config, storagePrefix);\n}\n\n", "import browser from 'webextension-polyfill';\n\nexport const Partition = {\n Instance: 'instance' as const,\n Common: 'common' as const\n};\nexport const Persistence = {\n Session: 'session' as const,\n Local: 'local' as const,\n None: 'none' as const\n};\n\ntype ConfigItem<T> = {\n default: T;\n partition?: typeof Partition[keyof typeof Partition]\n persist?: typeof Persistence[keyof typeof Persistence]\n}\n\n// export type Config = typeof StateConfig;\n\ntype DerivedState<T extends Record<string, ConfigItem<any>>> = {\n [P in keyof T]: T[P]['default'];\n};\ntype DerivedInstanceState<T extends Record<string, ConfigItem<any>>> = {\n [P in keyof T as T[P]['partition'] extends 'instance' ? P : never]: T[P]['default'];\n};\ntype DerivedCommonState<T extends Record<string, ConfigItem<any>>> = {\n [P in keyof T as T[P]['partition'] extends 'common' ? P : never]: T[P]['default'];\n};\n\ntype StateSubscriber<TConfig extends Record<string, ConfigItem<any>>> = {\n keys?: Array<keyof DerivedState<TConfig>>;\n callback: (changes: StateUpdate<TConfig>) => void;\n}\n\ntype CrannAgent<TConfig extends Record<string, ConfigItem<any>>> = {\n get: () => DerivedCommonState<TConfig> & DerivedInstanceState<TConfig>;\n set: (update: StateUpdate<TConfig>) => void;\n subscribe: (callback: (changes: StateUpdate<TConfig>) => void, keys?: Array<keyof TConfig>) => number;\n unsubscribe: (id: number) => void;\n}\n\ntype AgentSubscription<TConfig extends Record<string, ConfigItem<any>>> = {\n (callback: (changes: StateUpdate<TConfig>) => void, key?: keyof DerivedState<TConfig>): number;\n}\n\ntype StateUpdate<TConfig extends Record<string, ConfigItem<any>>> = Partial<DerivedState<TConfig>>;\n\nexport { ConfigItem, DerivedState, DerivedInstanceState, DerivedCommonState, StateSubscriber, CrannAgent, AgentSubscription, StateUpdate, DerivedState as State };\n", "export function deepEqual(a: any, b: any): boolean {\n if (a === b) return true;\n\n if (a == null || typeof a !== 'object' || b == null || typeof b !== 'object') return false;\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n\n if (keysA.length !== keysB.length) return false;\n\n keysA.sort();\n keysB.sort();\n\n for (let i = 0; i < keysA.length; i++) {\n const key = keysA[i];\n if (key !== keysB[i] || !deepEqual(a[key], b[key])) return false;\n }\n return true;\n}", "import { ConfigItem, CrannAgent, DerivedState, StateSubscriber, DerivedInstanceState, DerivedCommonState } from \"./model/crann.model\";\nimport { connect as connectPorter } from 'porter-source'\n\nexport function connect<TConfig extends Record<string, ConfigItem<any>>>(config: TConfig, context?: string): CrannAgent<TConfig> {\n const [post, setMessages] = connectPorter({ namespace: 'crann' });\n let _state = getDerivedState(config);\n let changes: Partial<DerivedState<TConfig>> | null = null;\n const listeners = new Map<number, StateSubscriber<TConfig>>();\n let listenerId = 0;\n setMessages({\n stateUpdate: (message) => {\n changes = message.payload.state;\n _state = { ..._state, ...changes };\n if (!!changes) {\n listeners.forEach(listener => {\n if (listener.keys === undefined) {\n listener.callback(changes!);\n } else {\n const matchFound = listener.keys.some(key => changes!.hasOwnProperty(key))\n matchFound && listener.callback(changes!);\n }\n });\n }\n }\n });\n\n const get = () => _state;\n const set = (newState: Partial<DerivedState<TConfig>>) => {\n post({ action: 'setState', payload: { state: newState } });\n }\n const subscribe = (callback: (changes: Partial<DerivedState<TConfig>>) => void, keys?: Array<keyof DerivedState<TConfig>>): number => {\n listeners.set(++listenerId, { keys, callback });\n return listenerId;\n }\n const unsubscribe = (id: number) => {\n listeners.delete(id);\n }\n return { get, set, subscribe, unsubscribe };\n}\n\nfunction getDerivedState<TConfig extends Record<string, ConfigItem<any>>>(config: TConfig): (DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>) {\n const instanceState = {} as DerivedInstanceState<TConfig>;\n\n Object.keys(config).forEach(key => {\n const item: ConfigItem<any> = config[key];\n if (item.partition === 'instance') {\n instanceState[key as keyof DerivedInstanceState<TConfig>] = item.default;\n }\n });\n\n const commonState = {} as DerivedCommonState<TConfig>;\n Object.keys(config).forEach(key => {\n const item: ConfigItem<any> = config[key];\n if (item.partition === 'instance') {\n commonState[key as keyof DerivedCommonState<TConfig>] = item.default;\n }\n });\n\n return { ...instanceState, ...commonState } as (DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>);\n}\n"],
5
- "mappings": "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",
6
- "names": ["src_exports", "__export", "Crann", "Partition", "Persistence", "connect", "create", "__toCommonJS", "import_webextension_polyfill", "Partition", "Persistence", "import_porter_source", "deepEqual", "b", "keysA", "keysB", "i", "key", "Crann", "config", "storagePrefix", "post", "_setMessages", "onConnect", "onDisconnect", "key", "connectionType", "context", "location", "initialInstanceState", "state", "update", "deepEqual", "currentState", "persistence", "value", "browser", "_", "listener", "changes", "instance", "worker", "itemKey", "item", "instanceItemKey", "instanceState", "Partition", "commonItemKey", "commonState", "local", "session", "combined", "prefixedKey", "create", "import_porter_source", "connect", "config", "context", "post", "setMessages", "connectPorter", "_state", "getDerivedState", "changes", "listeners", "listenerId", "message", "listener", "key", "newState", "callback", "keys", "id", "instanceState", "item", "commonState"]
4
+ "sourcesContent": ["export { create, Crann } from './crann';\nexport { connect, connected } from './crannAgent';\nexport { CrannAgent, StateUpdate, State, Partition, Persistence, ConfigItem, DerivedState, } from './model/crann.model';", "import browser from 'webextension-polyfill';\nimport { DerivedInstanceState, DerivedCommonState, ConfigItem, DerivedState, Partition } from './model/crann.model';\nimport { source, getMetadata, getKey, getTarget } from 'porter-source';\nimport { deepEqual } from './utils/deepEqual';\nimport { AgentLocation, AgentMetadata, Message, PorterContext, PostTarget } from 'porter-source';\n\nexport class Crann<TConfig extends Record<string, ConfigItem<any>>> {\n private static instance: Crann<any> | null = null;\n private instances: Map<string, DerivedInstanceState<TConfig>> = new Map();\n private defaultCommonState: DerivedCommonState<TConfig>;\n private defaultInstanceState: DerivedInstanceState<TConfig>;\n private commonState: DerivedCommonState<TConfig>;\n private stateChangeListeners: Array<(state: (DerivedInstanceState<TConfig> | DerivedState<TConfig>), changes: Partial<DerivedCommonState<TConfig> & DerivedInstanceState<TConfig>>, agent?: AgentMetadata) => void> = [];\n private storagePrefix = 'crann_';\n private post: (message: Message<any>, target?: PostTarget) => void = () => { };\n\n constructor(private config: TConfig, storagePrefix?: string) {\n this.log('Constructing');\n this.defaultInstanceState = this.initializeInstanceDefault();\n this.defaultCommonState = this.commonState = this.initializeCommonDefault();\n this.hydrate();\n this.log('Initializing porter');\n const [post, setMessages, onConnect, onDisconnect, onMessagesSet] = source('crann');\n this.post = post;\n setMessages({\n setState: (message, agent) => {\n if (!agent) {\n this.log('setState message heard from unknown agent');\n return;\n }\n this.instanceLog('Setting state: ', agent.key, message);\n this.set(message.payload.state, agent.key);\n }\n });\n onMessagesSet((agent) => {\n this.instanceLog('Messages set received. Sending initial state.', agent.key, agent);\n const fullState = this.get(agent.key);\n this.post({ action: 'initialState', payload: { state: fullState, key: agent.key } }, agent.key);\n });\n onConnect(({ key, connectionType, context, location }) => {\n this.instanceLog('Agent connected. Connection type, context and location: ', key, connectionType, context, location);\n this.addInstance(key);\n onDisconnect(({ key, connectionType, context, location }) => {\n this.instanceLog('Agent disconnect heard. Connection type, context and location: ', key, connectionType, context, location);\n this.removeInstance(key);\n });\n });\n this.storagePrefix = storagePrefix ?? this.storagePrefix;\n }\n\n\n public static getInstance<TConfig extends Record<string, ConfigItem<any>>>(config: TConfig, storagePrefix?: string): Crann<TConfig> {\n if (!Crann.instance) {\n Crann.instance = new Crann(config, storagePrefix);\n } else {\n console.log('CrannSource [static-core], Instance requested and already existed, returning');\n }\n return Crann.instance;\n }\n\n private async addInstance(key: string): Promise<void> {\n if (!this.instances.has(key)) {\n this.instanceLog('Adding instance from agent key: ', key);\n const initialInstanceState = { ...this.defaultInstanceState } as DerivedInstanceState<TConfig>;\n this.instances.set(key, initialInstanceState);\n } else {\n this.instanceLog('Instance was already registered, ignoring request from key: ', key);\n }\n }\n\n private async removeInstance(key: string): Promise<void> {\n if (this.instances.has(key)) {\n this.instanceLog('Remove instance requested. ', key);\n this.instances.delete(key);\n } else {\n this.instanceLog('Remove instance requested but it did not exist!. ', key);\n }\n }\n\n public async setCommonState(state: Partial<DerivedCommonState<TConfig>>): Promise<void> {\n this.log('Request to set common state with: ', state);\n const update = { ...this.commonState, ...state };\n if (!deepEqual(this.commonState, update)) {\n this.log('Confirmed new state was different than existing so proceeding to persist then notify all connected instances.');\n this.commonState = update;\n await this.persist(state);\n this.notify(state as Partial<DerivedState<TConfig>>);\n } else {\n this.log('New state seems to be the same as existing, skipping');\n }\n }\n\n public async setInstanceState(key: string, state: Partial<DerivedInstanceState<TConfig>>): Promise<void> {\n this.instanceLog('Request to update instance state, update: ', key, state);\n const currentState = this.instances.get(key) || this.defaultInstanceState;\n const update = { ...currentState, ...state };\n if (!deepEqual(currentState, update)) {\n this.instanceLog('Instance state update is different, updating and notifying. ', key);\n this.instances.set(key, update);\n this.notify(state as Partial<DerivedState<TConfig>>, key);\n } else {\n this.instanceLog('Instance state update is not different, skipping update. ', key);\n }\n }\n\n // If we pass in specific state to persist, it only persists that state. \n // Otherwise persists all of the worker state.\n private async persist(state?: Partial<DerivedCommonState<TConfig>>): Promise<void> {\n this.log('Persisting state');\n let wasPersisted = false;\n for (const key in (state || this.commonState)) {\n const item = this.config[key] as ConfigItem<any>;\n const persistence = item.persist || 'none';\n const value = state ? state[key as keyof DerivedCommonState<TConfig>] : this.commonState[key];\n switch (persistence) {\n case 'session':\n await browser.storage.session.set({ [this.storagePrefix + (key as string)]: value });\n wasPersisted = true;\n break;\n case 'local':\n await browser.storage.local.set({ [this.storagePrefix + (key as string)]: value });\n wasPersisted = true;\n break;\n default:\n break;\n }\n }\n if (wasPersisted) {\n this.log('State was persisted');\n } else {\n this.log('Nothing to persist');\n }\n }\n\n public async clear(): Promise<void> {\n this.log('Clearing state');\n this.commonState = this.defaultCommonState;\n this.instances.forEach((_, key) => {\n this.instances.set(key, this.defaultInstanceState);\n });\n await this.persist();\n this.notify({});\n }\n\n public subscribe(listener: (state: (DerivedInstanceState<TConfig> | DerivedState<TConfig>), changes: Partial<DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>>, agent?: AgentMetadata) => void): void {\n this.log('Subscribing to state');\n this.stateChangeListeners.push(listener);\n }\n\n // Right now we notify the instance even if the state change came from the instance.\n // This should probably be skipped for instance state, since it already knows.\n private notify(changes: Partial<DerivedState<TConfig>>, key?: string): void {\n const agentMeta = key ? getMetadata(key) : undefined;\n const target = agentMeta ? getTarget(agentMeta) : undefined;\n const state = key ? this.get(key) : this.get();\n\n if (this.stateChangeListeners.length > 0) {\n this.log('Notifying state change listeners in source');\n this.stateChangeListeners.forEach(listener => {\n listener(state, changes, agentMeta || undefined);\n });\n }\n\n if (key && target) {\n this.instanceLog('Notifying of state change.', key);\n this.post({ action: 'stateUpdate', payload: { state: changes } }, target);\n } else {\n console.log('Notifying everyone');\n // for every key of this.instances, post the state update to the corresponding key\n this.instances.forEach((_, key) => {\n this.post({ action: 'stateUpdate', payload: { state: changes } }, key);\n });\n }\n }\n\n public get(): DerivedState<TConfig>;\n public get(key: string): DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>;\n public get(key?: string): (DerivedCommonState<TConfig> | DerivedState<TConfig>) {\n if (!key) {\n return { ...this.commonState, ...{} as DerivedInstanceState<TConfig> };\n }\n return { ...this.commonState, ...this.instances.get(key) };\n }\n\n public findInstance(context: PorterContext, location: AgentLocation): string | null {\n // Todo: This feels like too-tight coupleing between porter and crann. Should be a better way.\n const searchKey = getKey({ context, ...location })\n for (const [key, instance] of this.instances) {\n if (key === searchKey) {\n this.log('Found instance for key: ', key);\n return key\n }\n }\n this.log('Could not find instance for context and location: ', context, location);\n return null;\n }\n\n public async set(state: Partial<DerivedCommonState<TConfig>>): Promise<void>\n public async set(state: Partial<DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>>, key: string): Promise<void>\n public async set(state: Partial<DerivedInstanceState<TConfig> | DerivedCommonState<TConfig>>, key?: string): Promise<void> {\n const instance = {} as Partial<DerivedInstanceState<TConfig>>;\n const worker = {} as Partial<DerivedCommonState<TConfig>>;\n\n for (const itemKey in state) {\n const item = this.config[itemKey as keyof TConfig] as ConfigItem<any>;\n if (item.partition === 'instance') {\n const instanceItemKey = itemKey as keyof DerivedInstanceState<TConfig>;\n const instanceState = state as Partial<DerivedInstanceState<TConfig>>;\n instance[instanceItemKey] = instanceState[instanceItemKey];\n } else if (!item.partition || item.partition === Partition.Common) {\n const commonItemKey = itemKey as keyof DerivedCommonState<TConfig>;\n const commonState = state as Partial<DerivedCommonState<TConfig>>;\n worker[commonItemKey] = commonState[commonItemKey]!;\n }\n }\n if (key && Object.keys(instance).length > 0) {\n this.instanceLog('Setting instance state: ', key, instance);\n this.setInstanceState(key, instance);\n }\n if (Object.keys(worker).length > 0) {\n this.log('Setting common state: ', worker);\n this.setCommonState(worker);\n }\n }\n\n private async hydrate(): Promise<void> {\n this.log('Hydrating state from storage.');\n const local = await browser.storage.local.get(null);\n const session = await browser.storage.session.get(null);\n const combined = { ...local, ...session };\n const update: Partial<DerivedCommonState<TConfig>> = {}; // Cast update as Partial<DerivedState<TConfig>>\n let hadItems = false;\n for (const prefixedKey in combined) {\n const key = this.removePrefix(prefixedKey);\n if (this.config.hasOwnProperty(key)) {\n const value = combined[key];\n update[key as keyof DerivedCommonState<TConfig>] = value;\n hadItems = true;\n }\n }\n if (hadItems) {\n this.log('Hydrated some items.');\n } else {\n this.log('No items found in storage.');\n }\n this.commonState = { ...this.defaultCommonState, ...update };\n }\n\n private removePrefix(key: string): string {\n if (key.startsWith(this.storagePrefix)) {\n return key.replace(this.storagePrefix, '');\n }\n return key;\n }\n\n private initializeInstanceDefault(): DerivedInstanceState<TConfig> {\n const instanceState: any = {};\n Object.keys(this.config).forEach(key => {\n const item: ConfigItem<any> = this.config[key];\n if (item.partition === 'instance') {\n instanceState[key] = item.default;\n }\n });\n return instanceState;\n }\n\n private initializeCommonDefault(): DerivedCommonState<TConfig> {\n const commonState: any = {};\n Object.keys(this.config).forEach(key => {\n const item: ConfigItem<any> = this.config[key];\n if (item.partition === Partition.Common) {\n commonState[key] = item.default;\n }\n });\n return commonState;\n }\n\n private log(message: string, ...args: any[]) {\n console.log(`CrannSource [core], ` + message, ...args);\n }\n private instanceLog(message: string, key: string, ...args: any[]) {\n console.log(`CrannSource [${key}], ` + message, ...args);\n }\n private error(message: string, ...args: any[]) {\n console.error(`CrannSource [core], ` + message, ...args);\n }\n private warn(message: string, ...args: any[]) {\n console.warn(`CrannSource [core], ` + message, ...args);\n }\n}\n\n\nexport function create<TConfig extends Record<string, ConfigItem<any>>>(config: TConfig, storagePrefix?: string): [\n (key?: string) => (DerivedState<TConfig>),\n (state: Partial<DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>>, key?: string) => Promise<void>,\n (listener: (state: (DerivedInstanceState<TConfig> | DerivedState<TConfig>), changes: Partial<DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>>, agent?: AgentMetadata) => void) => void,\n (context: PorterContext, location: AgentLocation) => string | null,\n] {\n const instance = Crann.getInstance(config, storagePrefix);\n return [\n instance.get.bind(instance),\n instance.set.bind(instance),\n instance.subscribe.bind(instance),\n instance.findInstance.bind(instance),\n ];\n}\n\n\n", "import browser from 'webextension-polyfill';\n\nexport const Partition = {\n Instance: 'instance' as const,\n Common: 'common' as const\n};\nexport const Persistence = {\n Session: 'session' as const,\n Local: 'local' as const,\n None: 'none' as const\n};\n\ntype ConfigItem<T> = {\n default: T;\n partition?: typeof Partition[keyof typeof Partition]\n persist?: typeof Persistence[keyof typeof Persistence]\n}\n\ntype AnyConfig = Record<string, ConfigItem<any>>;\n\n// export type Config = typeof StateConfig;\n\ntype DerivedState<T extends AnyConfig> = {\n [P in keyof T]: T[P]['default'];\n};\ntype DerivedInstanceState<T extends AnyConfig> = {\n [P in keyof T as T[P]['partition'] extends 'instance' ? P : never]: T[P]['default'];\n};\ntype DerivedCommonState<T extends AnyConfig> = {\n [P in keyof T as T[P]['partition'] extends 'common' ? P : never]: T[P]['default'];\n};\n\ntype StateSubscriber<TConfig extends AnyConfig> = {\n keys?: Array<keyof DerivedState<TConfig>>;\n callback: (changes: StateUpdate<TConfig>) => void;\n}\n\ntype CrannAgent<TConfig extends AnyConfig> = {\n get: () => DerivedState<TConfig>;\n set: (update: StateUpdate<TConfig>) => void;\n subscribe: (callback: (changes: StateUpdate<TConfig>) => void, keys?: Array<keyof TConfig>) => () => void;\n}\n\ntype UseCrann<TConfig extends AnyConfig> = <K extends keyof DerivedState<TConfig>>(\n key: K\n) => [\n DerivedState<TConfig>[K],\n (value: DerivedState<TConfig>[K]) => void,\n (callback: (value: DerivedState<TConfig>[K]) => void) => () => void\n ];\n\ntype ConnectReturn<TConfig extends AnyConfig> = [\n UseCrann<TConfig>,\n CrannAgent<TConfig>['get'],\n CrannAgent<TConfig>['set'],\n CrannAgent<TConfig>['subscribe']\n];\n\ntype AgentSubscription<TConfig extends AnyConfig> = {\n (callback: (changes: StateUpdate<TConfig>) => void, key?: keyof DerivedState<TConfig>): number;\n}\n\ntype StateUpdate<TConfig extends AnyConfig> = Partial<DerivedState<TConfig>>;\n\nexport { AnyConfig, ConfigItem, DerivedState, DerivedInstanceState, DerivedCommonState, StateSubscriber, CrannAgent, AgentSubscription, StateUpdate, DerivedState as State, ConnectReturn, UseCrann };\n", "export function deepEqual(a: any, b: any): boolean {\n if (a === b) return true;\n\n if (a == null || typeof a !== 'object' || b == null || typeof b !== 'object') return false;\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n\n if (keysA.length !== keysB.length) return false;\n\n keysA.sort();\n keysB.sort();\n\n for (let i = 0; i < keysA.length; i++) {\n const key = keysA[i];\n if (key !== keysB[i] || !deepEqual(a[key], b[key])) return false;\n }\n return true;\n}", "import { match } from \"assert\";\nimport { ConfigItem, CrannAgent, DerivedState, StateSubscriber, DerivedInstanceState, DerivedCommonState, ConnectReturn, UseCrann } from \"./model/crann.model\";\nimport { connect as connectPorter, PorterContext } from 'porter-source'\n\nlet crannInstance: unknown = null;\n\nexport function connect<TConfig extends Record<string, ConfigItem<any>>>(config: TConfig, context?: string): ConnectReturn<TConfig> {\n let _myKey = 'unset';\n log('Initializing with context: ', context);\n if (crannInstance) {\n log('We had an instance already, returning');\n return crannInstance as ConnectReturn<TConfig>;\n }\n log('No existing instance, creating a new one');\n const [post, setMessages] = connectPorter({ namespace: 'crann', agentContext: context as PorterContext });\n setMessages({\n initialState: (message) => {\n _state = message.payload.state;\n _myKey = message.payload.key;\n listeners.forEach(listener => {\n listener.callback(_state);\n });\n log(`Initial state received and ${listeners.size} listeners notified`);\n },\n stateUpdate: (message) => {\n log('State updated: ', message);\n changes = message.payload.state;\n _state = { ..._state, ...changes };\n if (!!changes) {\n log('Notifying listeners of state update');\n listeners.forEach(listener => {\n if (listener.keys === undefined) {\n log('Found a universal listener, notifying');\n listener.callback(changes!);\n } else {\n const matchFound = listener.keys.some(key => changes!.hasOwnProperty(key));\n if (matchFound) {\n log('Found a specific listener for this item, notifying');\n listener.callback(changes!);\n }\n }\n });\n }\n }\n });\n log('Porter connected. Setting up state and listeners');\n let _state = getDerivedState(config);\n let changes: Partial<DerivedState<TConfig>> | null = null;\n const listeners = new Set<StateSubscriber<TConfig>>();\n\n log('Completed setup, returning instance');\n\n const get = () => _state;\n const set = (newState: Partial<DerivedState<TConfig>>) => {\n console.log('CrannAgent, calling post with setState');\n post({ action: 'setState', payload: { state: newState } });\n }\n const subscribe = (callback: (changes: Partial<DerivedState<TConfig>>) => void, keys?: Array<keyof DerivedState<TConfig>>): () => void => {\n const listener = { keys, callback };\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n }\n }\n\n const useCrann: UseCrann<TConfig> = <K extends keyof DerivedState<TConfig>>(\n key: K\n ) => {\n const getValue = () => get()[key] as (DerivedState<TConfig>);\n const setValue = (value: DerivedState<TConfig>[K]) =>\n set({ [key]: value } as Partial<DerivedState<TConfig>>);\n const subscribeToChanges = (callback: (value: DerivedState<TConfig>[K]) => void) => {\n return subscribe((changes) => {\n if (key in changes) {\n callback(changes[key] as DerivedState<TConfig>[K]);\n }\n }, [key]);\n };\n\n return [getValue(), setValue, subscribeToChanges];\n }\n\n function log(message: string, ...args: any[]) {\n console.log(`CrannAgent [${_myKey}] ` + message, ...args);\n }\n\n const instance: ConnectReturn<TConfig> = [useCrann, get, set, subscribe];\n crannInstance = instance;\n\n return crannInstance as ConnectReturn<TConfig>;\n};\n\nexport function connected(): boolean {\n return crannInstance !== null;\n}\n\nfunction getDerivedState<TConfig extends Record<string, ConfigItem<any>>>(config: TConfig): (DerivedState<TConfig>) {\n const instanceState = {} as DerivedInstanceState<TConfig>;\n\n Object.keys(config).forEach(key => {\n const item: ConfigItem<any> = config[key];\n if (item.partition === 'instance') {\n instanceState[key as keyof DerivedInstanceState<TConfig>] = item.default;\n }\n });\n\n const commonState = {} as DerivedCommonState<TConfig>;\n Object.keys(config).forEach(key => {\n const item: ConfigItem<any> = config[key];\n if (item.partition === 'instance') {\n commonState[key as keyof DerivedCommonState<TConfig>] = item.default;\n }\n });\n\n return { ...instanceState, ...commonState } as unknown as (DerivedState<TConfig>);\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["src_exports", "__export", "Crann", "Partition", "Persistence", "connect", "connected", "create", "__toCommonJS", "import_webextension_polyfill", "Partition", "Persistence", "import_porter_source", "deepEqual", "b", "keysA", "keysB", "key", "_Crann", "config", "storagePrefix", "post", "setMessages", "onConnect", "onDisconnect", "onMessagesSet", "message", "agent", "fullState", "key", "connectionType", "context", "location", "initialInstanceState", "state", "update", "deepEqual", "currentState", "wasPersisted", "persistence", "value", "browser", "_", "listener", "changes", "agentMeta", "target", "searchKey", "instance", "worker", "itemKey", "item", "instanceItemKey", "instanceState", "Partition", "commonItemKey", "commonState", "local", "session", "combined", "hadItems", "prefixedKey", "args", "Crann", "create", "import_porter_source", "crannInstance", "connect", "config", "context", "_myKey", "log", "post", "setMessages", "connectPorter", "message", "_state", "listeners", "listener", "changes", "key", "getDerivedState", "get", "set", "newState", "subscribe", "callback", "keys", "useCrann", "getValue", "setValue", "value", "subscribeToChanges", "args", "connected", "instanceState", "item", "commonState"]
7
7
  }
package/dist/esm/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import C from"webextension-polyfill";var g={Instance:"instance",Common:"common"},h={Session:"session",Local:"local",None:"none"};import{source as y}from"porter-source";function d(o,t){if(o===t)return!0;if(o==null||typeof o!="object"||t==null||typeof t!="object")return!1;let e=Object.keys(o),n=Object.keys(t);if(e.length!==n.length)return!1;e.sort(),n.sort();for(let i=0;i<e.length;i++){let a=e[i];if(a!==n[i]||!d(o[a],t[a]))return!1}return!0}var l=class{constructor(t,e){this.config=t;this.instances=new Map;this.stateChangeListeners=[];this.storagePrefix="crann_";this.post=()=>{};console.log("Crann constructor"),this.defaultInstanceState=this.initializeInstanceDefault(),this.defaultCommonState=this.commonState=this.initializeCommonDefault(),this.hydrate();let[n,i,a,s]=y("crann");this.post=n,a(({key:r,connectionType:m,context:v,location:p})=>{console.log("Crann porter connect",r,m,v,p),this.addInstance(r),s(({key:S,connectionType:c,context:f,location:u})=>{console.log("Crann porter connect",S,c,f,u),this.removeInstance(S)})}),this.storagePrefix=e!=null?e:this.storagePrefix}async addInstance(t){if(this.instances.has(t))console.warn("Crann instance already exists",t);else{let e={...this.defaultInstanceState};this.instances.set(t,e)}}async removeInstance(t){this.instances.has(t)?this.instances.delete(t):console.warn("Crann instance does not exist",t)}async setCommonState(t){let e={...this.commonState,...t};d(this.commonState,e)||(this.commonState=e,await this.persist(t),this.notify(t))}async setInstanceState(t,e){let n=this.instances.get(t)||this.defaultInstanceState,i={...n,...e};d(n,i)||(this.instances.set(t,i),this.notify(e,t))}async persist(t){for(let e in t||this.commonState){let i=this.config[e].persist||"none",a=t?t[e]:this.commonState[e];switch(i){case"session":await C.storage.session.set({[this.storagePrefix+e]:a});break;case"local":await C.storage.local.set({[this.storagePrefix+e]:a});break;default:break}}}async clear(){this.commonState=this.defaultCommonState,this.instances.forEach((t,e)=>{this.instances.set(e,this.defaultInstanceState)}),await this.persist(),this.notify({})}subscribe(t){this.stateChangeListeners.push(t)}notify(t,e){let n=e?this.get(e):this.get();this.stateChangeListeners.forEach(i=>i(n,t,e)),e?this.post({action:"stateUpdate",payload:{state:t}},e):this.instances.forEach((i,a)=>{this.post({action:"stateUpdate",payload:{state:t}},a)})}get(t){return t?{...this.commonState,...this.instances.get(t)}:{...this.commonState}}async set(t,e){let n={},i={};for(let a in t){let s=this.config[a];if(s.partition==="instance"){let r=a,m=t;n[r]=m[r]}else if(!s.partition||s.partition===g.Common){let r=a,m=t;i[r]=m[r]}}e&&this.setInstanceState(e,n),this.setCommonState(i)}async hydrate(){let t=await C.storage.local.get(null),e=await C.storage.session.get(null),n={...t,...e},i={};for(let a in n){let s=this.removePrefix(a);if(this.config.hasOwnProperty(s)){let r=n[s];i[s]=r}}this.commonState={...this.defaultCommonState,...i}}removePrefix(t){return t.startsWith(this.storagePrefix)?t.replace(this.storagePrefix,""):t}initializeInstanceDefault(){let t={};return Object.keys(this.config).forEach(e=>{let n=this.config[e];n.partition==="instance"&&(t[e]=n.default)}),t}initializeCommonDefault(){let t={};return Object.keys(this.config).forEach(e=>{let n=this.config[e];n.partition===g.Common&&(t[e]=n.default)}),t}};function D(o,t){return new l(o,t)}import{connect as I}from"porter-source";function b(o,t){let[e,n]=I({namespace:"crann"}),i=P(o),a=null,s=new Map,r=0;return n({stateUpdate:c=>{a=c.payload.state,i={...i,...a},a&&s.forEach(f=>{(f.keys===void 0||f.keys.some(T=>a.hasOwnProperty(T)))&&f.callback(a)})}}),{get:()=>i,set:c=>{e({action:"setState",payload:{state:c}})},subscribe:(c,f)=>(s.set(++r,{keys:f,callback:c}),r),unsubscribe:c=>{s.delete(c)}}}function P(o){let t={};Object.keys(o).forEach(n=>{let i=o[n];i.partition==="instance"&&(t[n]=i.default)});let e={};return Object.keys(o).forEach(n=>{let i=o[n];i.partition==="instance"&&(e[n]=i.default)}),{...t,...e}}export{l as Crann,g as Partition,h as Persistence,b as connect,D as create};
1
+ import h from"webextension-polyfill";var v={Instance:"instance",Common:"common"},x={Session:"session",Local:"local",None:"none"};import{source as k,getMetadata as A,getKey as w,getTarget as L}from"porter-source";function p(g,t){if(g===t)return!0;if(g==null||typeof g!="object"||t==null||typeof t!="object")return!1;let e=Object.keys(g),n=Object.keys(t);if(e.length!==n.length)return!1;e.sort(),n.sort();for(let i=0;i<e.length;i++){let a=e[i];if(a!==n[i]||!p(g[a],t[a]))return!1}return!0}var C=class C{constructor(t,e){this.config=t;this.instances=new Map;this.stateChangeListeners=[];this.storagePrefix="crann_";this.post=()=>{};this.log("Constructing"),this.defaultInstanceState=this.initializeInstanceDefault(),this.defaultCommonState=this.commonState=this.initializeCommonDefault(),this.hydrate(),this.log("Initializing porter");let[n,i,a,o,r]=k("crann");this.post=n,i({setState:(s,l)=>{if(!l){this.log("setState message heard from unknown agent");return}this.instanceLog("Setting state: ",l.key,s),this.set(s.payload.state,l.key)}}),r(s=>{this.instanceLog("Messages set received. Sending initial state.",s.key,s);let l=this.get(s.key);this.post({action:"initialState",payload:{state:l,key:s.key}},s.key)}),a(({key:s,connectionType:l,context:S,location:T})=>{this.instanceLog("Agent connected. Connection type, context and location: ",s,l,S,T),this.addInstance(s),o(({key:d,connectionType:P,context:c,location:f})=>{this.instanceLog("Agent disconnect heard. Connection type, context and location: ",d,P,c,f),this.removeInstance(d)})}),this.storagePrefix=e!=null?e:this.storagePrefix}static getInstance(t,e){return C.instance?console.log("CrannSource [static-core], Instance requested and already existed, returning"):C.instance=new C(t,e),C.instance}async addInstance(t){if(this.instances.has(t))this.instanceLog("Instance was already registered, ignoring request from key: ",t);else{this.instanceLog("Adding instance from agent key: ",t);let e={...this.defaultInstanceState};this.instances.set(t,e)}}async removeInstance(t){this.instances.has(t)?(this.instanceLog("Remove instance requested. ",t),this.instances.delete(t)):this.instanceLog("Remove instance requested but it did not exist!. ",t)}async setCommonState(t){this.log("Request to set common state with: ",t);let e={...this.commonState,...t};p(this.commonState,e)?this.log("New state seems to be the same as existing, skipping"):(this.log("Confirmed new state was different than existing so proceeding to persist then notify all connected instances."),this.commonState=e,await this.persist(t),this.notify(t))}async setInstanceState(t,e){this.instanceLog("Request to update instance state, update: ",t,e);let n=this.instances.get(t)||this.defaultInstanceState,i={...n,...e};p(n,i)?this.instanceLog("Instance state update is not different, skipping update. ",t):(this.instanceLog("Instance state update is different, updating and notifying. ",t),this.instances.set(t,i),this.notify(e,t))}async persist(t){this.log("Persisting state");let e=!1;for(let n in t||this.commonState){let a=this.config[n].persist||"none",o=t?t[n]:this.commonState[n];switch(a){case"session":await h.storage.session.set({[this.storagePrefix+n]:o}),e=!0;break;case"local":await h.storage.local.set({[this.storagePrefix+n]:o}),e=!0;break;default:break}}e?this.log("State was persisted"):this.log("Nothing to persist")}async clear(){this.log("Clearing state"),this.commonState=this.defaultCommonState,this.instances.forEach((t,e)=>{this.instances.set(e,this.defaultInstanceState)}),await this.persist(),this.notify({})}subscribe(t){this.log("Subscribing to state"),this.stateChangeListeners.push(t)}notify(t,e){let n=e?A(e):void 0,i=n?L(n):void 0,a=e?this.get(e):this.get();this.stateChangeListeners.length>0&&(this.log("Notifying state change listeners in source"),this.stateChangeListeners.forEach(o=>{o(a,t,n||void 0)})),e&&i?(this.instanceLog("Notifying of state change.",e),this.post({action:"stateUpdate",payload:{state:t}},i)):(console.log("Notifying everyone"),this.instances.forEach((o,r)=>{this.post({action:"stateUpdate",payload:{state:t}},r)}))}get(t){return t?{...this.commonState,...this.instances.get(t)}:{...this.commonState}}findInstance(t,e){let n=w({context:t,...e});for(let[i,a]of this.instances)if(i===n)return this.log("Found instance for key: ",i),i;return this.log("Could not find instance for context and location: ",t,e),null}async set(t,e){let n={},i={};for(let a in t){let o=this.config[a];if(o.partition==="instance"){let r=a,s=t;n[r]=s[r]}else if(!o.partition||o.partition===v.Common){let r=a,s=t;i[r]=s[r]}}e&&Object.keys(n).length>0&&(this.instanceLog("Setting instance state: ",e,n),this.setInstanceState(e,n)),Object.keys(i).length>0&&(this.log("Setting common state: ",i),this.setCommonState(i))}async hydrate(){this.log("Hydrating state from storage.");let t=await h.storage.local.get(null),e=await h.storage.session.get(null),n={...t,...e},i={},a=!1;for(let o in n){let r=this.removePrefix(o);if(this.config.hasOwnProperty(r)){let s=n[r];i[r]=s,a=!0}}a?this.log("Hydrated some items."):this.log("No items found in storage."),this.commonState={...this.defaultCommonState,...i}}removePrefix(t){return t.startsWith(this.storagePrefix)?t.replace(this.storagePrefix,""):t}initializeInstanceDefault(){let t={};return Object.keys(this.config).forEach(e=>{let n=this.config[e];n.partition==="instance"&&(t[e]=n.default)}),t}initializeCommonDefault(){let t={};return Object.keys(this.config).forEach(e=>{let n=this.config[e];n.partition===v.Common&&(t[e]=n.default)}),t}log(t,...e){console.log("CrannSource [core], "+t,...e)}instanceLog(t,e,...n){console.log(`CrannSource [${e}], `+t,...n)}error(t,...e){console.error("CrannSource [core], "+t,...e)}warn(t,...e){console.warn("CrannSource [core], "+t,...e)}};C.instance=null;var y=C;function K(g,t){let e=y.getInstance(g,t);return[e.get.bind(e),e.set.bind(e),e.subscribe.bind(e),e.findInstance.bind(e)]}import{connect as R}from"porter-source";var u=null;function U(g,t){let e="unset";if(d("Initializing with context: ",t),u)return d("We had an instance already, returning"),u;d("No existing instance, creating a new one");let[n,i]=R({namespace:"crann",agentContext:t});i({initialState:c=>{a=c.payload.state,e=c.payload.key,r.forEach(f=>{f.callback(a)}),d(`Initial state received and ${r.size} listeners notified`)},stateUpdate:c=>{d("State updated: ",c),o=c.payload.state,a={...a,...o},o&&(d("Notifying listeners of state update"),r.forEach(f=>{f.keys===void 0?(d("Found a universal listener, notifying"),f.callback(o)):f.keys.some(D=>o.hasOwnProperty(D))&&(d("Found a specific listener for this item, notifying"),f.callback(o))}))}}),d("Porter connected. Setting up state and listeners");let a=E(g),o=null,r=new Set;d("Completed setup, returning instance");let s=()=>a,l=c=>{console.log("CrannAgent, calling post with setState"),n({action:"setState",payload:{state:c}})},S=(c,f)=>{let m={keys:f,callback:c};return r.add(m),()=>{r.delete(m)}},T=c=>{let f=()=>s()[c],m=I=>l({[c]:I}),D=I=>S(b=>{c in b&&I(b[c])},[c]);return[f(),m,D]};function d(c,...f){console.log(`CrannAgent [${e}] `+c,...f)}return u=[T,s,l,S],u}function M(){return u!==null}function E(g){let t={};Object.keys(g).forEach(n=>{let i=g[n];i.partition==="instance"&&(t[n]=i.default)});let e={};return Object.keys(g).forEach(n=>{let i=g[n];i.partition==="instance"&&(e[n]=i.default)}),{...t,...e}}export{y as Crann,v as Partition,x as Persistence,U as connect,M as connected,K as create};
2
2
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/crann.ts", "../../src/model/crann.model.ts", "../../src/utils/deepEqual.ts", "../../src/crannAgent.ts"],
4
- "sourcesContent": ["import browser from 'webextension-polyfill';\nimport { DerivedInstanceState, DerivedCommonState, ConfigItem, DerivedState, Partition, CrannAgent, StateSubscriber } from './model/crann.model';\nimport { source, } from 'porter-source';\nimport { deepEqual } from './utils/deepEqual';\nimport { AgentLocation, AgentMetadata } from 'porter-source/dist/types/porter.model';\n\nexport class Crann<TConfig extends Record<string, ConfigItem<any>>> {\n private instances: Map<string, DerivedInstanceState<TConfig>> = new Map();\n private defaultCommonState: DerivedCommonState<TConfig>;\n private defaultInstanceState: DerivedInstanceState<TConfig>;\n private commonState: DerivedCommonState<TConfig>;\n private stateChangeListeners: Array<(state: (DerivedInstanceState<TConfig> | DerivedState<TConfig>), changes: Partial<DerivedCommonState<TConfig> & DerivedInstanceState<TConfig>>, key?: string) => void> = [];\n private storagePrefix = 'crann_';\n private post: (message: any, contextOrKey: string, location?: Partial<AgentLocation>) => void = () => { };\n\n constructor(private config: TConfig, storagePrefix?: string) {\n console.log('Crann constructor');\n this.defaultInstanceState = this.initializeInstanceDefault();\n this.defaultCommonState = this.commonState = this.initializeCommonDefault();\n this.hydrate();\n const [post, _setMessages, onConnect, onDisconnect] = source('crann');\n this.post = post;\n onConnect(({ key, connectionType, context, location }) => {\n console.log('Crann porter connect', key, connectionType, context, location);\n this.addInstance(key);\n onDisconnect(({ key, connectionType, context, location }) => {\n console.log('Crann porter connect', key, connectionType, context, location);\n this.removeInstance(key);\n });\n });\n this.storagePrefix = storagePrefix ?? this.storagePrefix;\n }\n\n private async addInstance(key: string): Promise<void> {\n if (!this.instances.has(key)) {\n const initialInstanceState = { ...this.defaultInstanceState } as DerivedInstanceState<TConfig>;\n this.instances.set(key, initialInstanceState);\n } else {\n console.warn('Crann instance already exists', key);\n }\n }\n\n private async removeInstance(key: string): Promise<void> {\n if (this.instances.has(key)) {\n this.instances.delete(key);\n } else {\n console.warn('Crann instance does not exist', key);\n }\n }\n\n public async setCommonState(state: Partial<DerivedCommonState<TConfig>>): Promise<void> {\n const update = { ...this.commonState, ...state };\n if (!deepEqual(this.commonState, update)) {\n this.commonState = update;\n await this.persist(state);\n this.notify(state as Partial<DerivedState<TConfig>>);\n }\n }\n\n public async setInstanceState(key: string, state: Partial<DerivedInstanceState<TConfig>>): Promise<void> {\n const currentState = this.instances.get(key) || this.defaultInstanceState;\n const update = { ...currentState, ...state };\n if (!deepEqual(currentState, update)) {\n this.instances.set(key, update);\n this.notify(state as Partial<DerivedState<TConfig>>, key);\n }\n }\n\n // If we pass in specific state to persist, it only persists that state. \n // Otherwise persists all of the worker state.\n private async persist(state?: Partial<DerivedCommonState<TConfig>>): Promise<void> {\n for (const key in (state || this.commonState)) {\n const item = this.config[key] as ConfigItem<any>;\n const persistence = item.persist || 'none';\n const value = state ? state[key as keyof DerivedCommonState<TConfig>] : this.commonState[key];\n switch (persistence) {\n case 'session':\n await browser.storage.session.set({ [this.storagePrefix + (key as string)]: value });\n break;\n case 'local':\n await browser.storage.local.set({ [this.storagePrefix + (key as string)]: value });\n break;\n default:\n break;\n }\n }\n }\n\n public async clear(): Promise<void> {\n this.commonState = this.defaultCommonState;\n this.instances.forEach((_, key) => {\n this.instances.set(key, this.defaultInstanceState);\n });\n await this.persist();\n this.notify({});\n }\n\n public subscribe(listener: (state: (DerivedInstanceState<TConfig> | DerivedState<TConfig>), changes: Partial<DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>>, key?: string) => void): void {\n this.stateChangeListeners.push(listener);\n }\n\n private notify(changes: Partial<DerivedState<TConfig>>, key?: string): void {\n const state = key ? this.get(key) : this.get();\n this.stateChangeListeners.forEach(listener => listener(state, changes, key));\n if (key) {\n this.post({ action: 'stateUpdate', payload: { state: changes } }, key);\n } else {\n // for every key of this.instances, post the state update to the corresponding key\n this.instances.forEach((_, key) => {\n this.post({ action: 'stateUpdate', payload: { state: changes } }, key);\n });\n }\n }\n\n public get(): DerivedState<TConfig>;\n public get(key: string): DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>;\n public get(key?: string): (DerivedCommonState<TConfig> | DerivedState<TConfig>) {\n if (!key) {\n return { ...this.commonState, ...{} as DerivedInstanceState<TConfig> };\n }\n return { ...this.commonState, ...this.instances.get(key) };\n }\n\n public async set(state: Partial<DerivedCommonState<TConfig>>): Promise<void>\n public async set(state: Partial<DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>>, key: string): Promise<void>\n public async set(state: Partial<DerivedInstanceState<TConfig> | DerivedCommonState<TConfig>>, key?: string): Promise<void> {\n const instance = {} as Partial<DerivedInstanceState<TConfig>>;\n const worker = {} as Partial<DerivedCommonState<TConfig>>;\n\n for (const itemKey in state) {\n const item = this.config[itemKey as keyof TConfig] as ConfigItem<any>;\n if (item.partition === 'instance') {\n const instanceItemKey = itemKey as keyof DerivedInstanceState<TConfig>;\n const instanceState = state as Partial<DerivedInstanceState<TConfig>>;\n instance[instanceItemKey] = instanceState[instanceItemKey];\n } else if (!item.partition || item.partition === Partition.Common) {\n const commonItemKey = itemKey as keyof DerivedCommonState<TConfig>;\n const commonState = state as Partial<DerivedCommonState<TConfig>>;\n worker[commonItemKey] = commonState[commonItemKey]!;\n }\n }\n if (key) this.setInstanceState(key, instance);\n this.setCommonState(worker);\n }\n\n private async hydrate(): Promise<void> {\n const local = await browser.storage.local.get(null);\n const session = await browser.storage.session.get(null);\n const combined = { ...local, ...session };\n const update: Partial<DerivedCommonState<TConfig>> = {}; // Cast update as Partial<DerivedState<TConfig>>\n for (const prefixedKey in combined) {\n const key = this.removePrefix(prefixedKey);\n if (this.config.hasOwnProperty(key)) {\n const value = combined[key];\n update[key as keyof DerivedCommonState<TConfig>] = value;\n }\n }\n this.commonState = { ...this.defaultCommonState, ...update };\n }\n\n private removePrefix(key: string): string {\n if (key.startsWith(this.storagePrefix)) {\n return key.replace(this.storagePrefix, '');\n }\n return key;\n }\n\n private initializeInstanceDefault(): DerivedInstanceState<TConfig> {\n const instanceState: any = {};\n Object.keys(this.config).forEach(key => {\n const item: ConfigItem<any> = this.config[key];\n if (item.partition === 'instance') {\n instanceState[key] = item.default;\n }\n });\n return instanceState;\n }\n\n private initializeCommonDefault(): DerivedCommonState<TConfig> {\n const commonState: any = {};\n Object.keys(this.config).forEach(key => {\n const item: ConfigItem<any> = this.config[key];\n if (item.partition === Partition.Common) {\n commonState[key] = item.default;\n }\n });\n return commonState;\n }\n}\n\n\nexport function create<TConfig extends Record<string, ConfigItem<any>>>(config: TConfig, storagePrefix?: string): Crann<TConfig> {\n return new Crann(config, storagePrefix);\n}\n\n", "import browser from 'webextension-polyfill';\n\nexport const Partition = {\n Instance: 'instance' as const,\n Common: 'common' as const\n};\nexport const Persistence = {\n Session: 'session' as const,\n Local: 'local' as const,\n None: 'none' as const\n};\n\ntype ConfigItem<T> = {\n default: T;\n partition?: typeof Partition[keyof typeof Partition]\n persist?: typeof Persistence[keyof typeof Persistence]\n}\n\n// export type Config = typeof StateConfig;\n\ntype DerivedState<T extends Record<string, ConfigItem<any>>> = {\n [P in keyof T]: T[P]['default'];\n};\ntype DerivedInstanceState<T extends Record<string, ConfigItem<any>>> = {\n [P in keyof T as T[P]['partition'] extends 'instance' ? P : never]: T[P]['default'];\n};\ntype DerivedCommonState<T extends Record<string, ConfigItem<any>>> = {\n [P in keyof T as T[P]['partition'] extends 'common' ? P : never]: T[P]['default'];\n};\n\ntype StateSubscriber<TConfig extends Record<string, ConfigItem<any>>> = {\n keys?: Array<keyof DerivedState<TConfig>>;\n callback: (changes: StateUpdate<TConfig>) => void;\n}\n\ntype CrannAgent<TConfig extends Record<string, ConfigItem<any>>> = {\n get: () => DerivedCommonState<TConfig> & DerivedInstanceState<TConfig>;\n set: (update: StateUpdate<TConfig>) => void;\n subscribe: (callback: (changes: StateUpdate<TConfig>) => void, keys?: Array<keyof TConfig>) => number;\n unsubscribe: (id: number) => void;\n}\n\ntype AgentSubscription<TConfig extends Record<string, ConfigItem<any>>> = {\n (callback: (changes: StateUpdate<TConfig>) => void, key?: keyof DerivedState<TConfig>): number;\n}\n\ntype StateUpdate<TConfig extends Record<string, ConfigItem<any>>> = Partial<DerivedState<TConfig>>;\n\nexport { ConfigItem, DerivedState, DerivedInstanceState, DerivedCommonState, StateSubscriber, CrannAgent, AgentSubscription, StateUpdate, DerivedState as State };\n", "export function deepEqual(a: any, b: any): boolean {\n if (a === b) return true;\n\n if (a == null || typeof a !== 'object' || b == null || typeof b !== 'object') return false;\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n\n if (keysA.length !== keysB.length) return false;\n\n keysA.sort();\n keysB.sort();\n\n for (let i = 0; i < keysA.length; i++) {\n const key = keysA[i];\n if (key !== keysB[i] || !deepEqual(a[key], b[key])) return false;\n }\n return true;\n}", "import { ConfigItem, CrannAgent, DerivedState, StateSubscriber, DerivedInstanceState, DerivedCommonState } from \"./model/crann.model\";\nimport { connect as connectPorter } from 'porter-source'\n\nexport function connect<TConfig extends Record<string, ConfigItem<any>>>(config: TConfig, context?: string): CrannAgent<TConfig> {\n const [post, setMessages] = connectPorter({ namespace: 'crann' });\n let _state = getDerivedState(config);\n let changes: Partial<DerivedState<TConfig>> | null = null;\n const listeners = new Map<number, StateSubscriber<TConfig>>();\n let listenerId = 0;\n setMessages({\n stateUpdate: (message) => {\n changes = message.payload.state;\n _state = { ..._state, ...changes };\n if (!!changes) {\n listeners.forEach(listener => {\n if (listener.keys === undefined) {\n listener.callback(changes!);\n } else {\n const matchFound = listener.keys.some(key => changes!.hasOwnProperty(key))\n matchFound && listener.callback(changes!);\n }\n });\n }\n }\n });\n\n const get = () => _state;\n const set = (newState: Partial<DerivedState<TConfig>>) => {\n post({ action: 'setState', payload: { state: newState } });\n }\n const subscribe = (callback: (changes: Partial<DerivedState<TConfig>>) => void, keys?: Array<keyof DerivedState<TConfig>>): number => {\n listeners.set(++listenerId, { keys, callback });\n return listenerId;\n }\n const unsubscribe = (id: number) => {\n listeners.delete(id);\n }\n return { get, set, subscribe, unsubscribe };\n}\n\nfunction getDerivedState<TConfig extends Record<string, ConfigItem<any>>>(config: TConfig): (DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>) {\n const instanceState = {} as DerivedInstanceState<TConfig>;\n\n Object.keys(config).forEach(key => {\n const item: ConfigItem<any> = config[key];\n if (item.partition === 'instance') {\n instanceState[key as keyof DerivedInstanceState<TConfig>] = item.default;\n }\n });\n\n const commonState = {} as DerivedCommonState<TConfig>;\n Object.keys(config).forEach(key => {\n const item: ConfigItem<any> = config[key];\n if (item.partition === 'instance') {\n commonState[key as keyof DerivedCommonState<TConfig>] = item.default;\n }\n });\n\n return { ...instanceState, ...commonState } as (DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>);\n}\n"],
5
- "mappings": "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",
6
- "names": ["browser", "Partition", "Persistence", "source", "deepEqual", "a", "b", "keysA", "keysB", "key", "Crann", "config", "storagePrefix", "post", "_setMessages", "onConnect", "onDisconnect", "source", "key", "connectionType", "context", "location", "initialInstanceState", "state", "update", "deepEqual", "currentState", "persistence", "value", "browser", "_", "listener", "changes", "instance", "worker", "itemKey", "item", "instanceItemKey", "instanceState", "Partition", "commonItemKey", "commonState", "local", "session", "combined", "prefixedKey", "create", "connectPorter", "connect", "config", "context", "post", "setMessages", "_state", "getDerivedState", "changes", "listeners", "listenerId", "message", "listener", "key", "newState", "callback", "keys", "id", "instanceState", "item", "commonState"]
4
+ "sourcesContent": ["import browser from 'webextension-polyfill';\nimport { DerivedInstanceState, DerivedCommonState, ConfigItem, DerivedState, Partition } from './model/crann.model';\nimport { source, getMetadata, getKey, getTarget } from 'porter-source';\nimport { deepEqual } from './utils/deepEqual';\nimport { AgentLocation, AgentMetadata, Message, PorterContext, PostTarget } from 'porter-source';\n\nexport class Crann<TConfig extends Record<string, ConfigItem<any>>> {\n private static instance: Crann<any> | null = null;\n private instances: Map<string, DerivedInstanceState<TConfig>> = new Map();\n private defaultCommonState: DerivedCommonState<TConfig>;\n private defaultInstanceState: DerivedInstanceState<TConfig>;\n private commonState: DerivedCommonState<TConfig>;\n private stateChangeListeners: Array<(state: (DerivedInstanceState<TConfig> | DerivedState<TConfig>), changes: Partial<DerivedCommonState<TConfig> & DerivedInstanceState<TConfig>>, agent?: AgentMetadata) => void> = [];\n private storagePrefix = 'crann_';\n private post: (message: Message<any>, target?: PostTarget) => void = () => { };\n\n constructor(private config: TConfig, storagePrefix?: string) {\n this.log('Constructing');\n this.defaultInstanceState = this.initializeInstanceDefault();\n this.defaultCommonState = this.commonState = this.initializeCommonDefault();\n this.hydrate();\n this.log('Initializing porter');\n const [post, setMessages, onConnect, onDisconnect, onMessagesSet] = source('crann');\n this.post = post;\n setMessages({\n setState: (message, agent) => {\n if (!agent) {\n this.log('setState message heard from unknown agent');\n return;\n }\n this.instanceLog('Setting state: ', agent.key, message);\n this.set(message.payload.state, agent.key);\n }\n });\n onMessagesSet((agent) => {\n this.instanceLog('Messages set received. Sending initial state.', agent.key, agent);\n const fullState = this.get(agent.key);\n this.post({ action: 'initialState', payload: { state: fullState, key: agent.key } }, agent.key);\n });\n onConnect(({ key, connectionType, context, location }) => {\n this.instanceLog('Agent connected. Connection type, context and location: ', key, connectionType, context, location);\n this.addInstance(key);\n onDisconnect(({ key, connectionType, context, location }) => {\n this.instanceLog('Agent disconnect heard. Connection type, context and location: ', key, connectionType, context, location);\n this.removeInstance(key);\n });\n });\n this.storagePrefix = storagePrefix ?? this.storagePrefix;\n }\n\n\n public static getInstance<TConfig extends Record<string, ConfigItem<any>>>(config: TConfig, storagePrefix?: string): Crann<TConfig> {\n if (!Crann.instance) {\n Crann.instance = new Crann(config, storagePrefix);\n } else {\n console.log('CrannSource [static-core], Instance requested and already existed, returning');\n }\n return Crann.instance;\n }\n\n private async addInstance(key: string): Promise<void> {\n if (!this.instances.has(key)) {\n this.instanceLog('Adding instance from agent key: ', key);\n const initialInstanceState = { ...this.defaultInstanceState } as DerivedInstanceState<TConfig>;\n this.instances.set(key, initialInstanceState);\n } else {\n this.instanceLog('Instance was already registered, ignoring request from key: ', key);\n }\n }\n\n private async removeInstance(key: string): Promise<void> {\n if (this.instances.has(key)) {\n this.instanceLog('Remove instance requested. ', key);\n this.instances.delete(key);\n } else {\n this.instanceLog('Remove instance requested but it did not exist!. ', key);\n }\n }\n\n public async setCommonState(state: Partial<DerivedCommonState<TConfig>>): Promise<void> {\n this.log('Request to set common state with: ', state);\n const update = { ...this.commonState, ...state };\n if (!deepEqual(this.commonState, update)) {\n this.log('Confirmed new state was different than existing so proceeding to persist then notify all connected instances.');\n this.commonState = update;\n await this.persist(state);\n this.notify(state as Partial<DerivedState<TConfig>>);\n } else {\n this.log('New state seems to be the same as existing, skipping');\n }\n }\n\n public async setInstanceState(key: string, state: Partial<DerivedInstanceState<TConfig>>): Promise<void> {\n this.instanceLog('Request to update instance state, update: ', key, state);\n const currentState = this.instances.get(key) || this.defaultInstanceState;\n const update = { ...currentState, ...state };\n if (!deepEqual(currentState, update)) {\n this.instanceLog('Instance state update is different, updating and notifying. ', key);\n this.instances.set(key, update);\n this.notify(state as Partial<DerivedState<TConfig>>, key);\n } else {\n this.instanceLog('Instance state update is not different, skipping update. ', key);\n }\n }\n\n // If we pass in specific state to persist, it only persists that state. \n // Otherwise persists all of the worker state.\n private async persist(state?: Partial<DerivedCommonState<TConfig>>): Promise<void> {\n this.log('Persisting state');\n let wasPersisted = false;\n for (const key in (state || this.commonState)) {\n const item = this.config[key] as ConfigItem<any>;\n const persistence = item.persist || 'none';\n const value = state ? state[key as keyof DerivedCommonState<TConfig>] : this.commonState[key];\n switch (persistence) {\n case 'session':\n await browser.storage.session.set({ [this.storagePrefix + (key as string)]: value });\n wasPersisted = true;\n break;\n case 'local':\n await browser.storage.local.set({ [this.storagePrefix + (key as string)]: value });\n wasPersisted = true;\n break;\n default:\n break;\n }\n }\n if (wasPersisted) {\n this.log('State was persisted');\n } else {\n this.log('Nothing to persist');\n }\n }\n\n public async clear(): Promise<void> {\n this.log('Clearing state');\n this.commonState = this.defaultCommonState;\n this.instances.forEach((_, key) => {\n this.instances.set(key, this.defaultInstanceState);\n });\n await this.persist();\n this.notify({});\n }\n\n public subscribe(listener: (state: (DerivedInstanceState<TConfig> | DerivedState<TConfig>), changes: Partial<DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>>, agent?: AgentMetadata) => void): void {\n this.log('Subscribing to state');\n this.stateChangeListeners.push(listener);\n }\n\n // Right now we notify the instance even if the state change came from the instance.\n // This should probably be skipped for instance state, since it already knows.\n private notify(changes: Partial<DerivedState<TConfig>>, key?: string): void {\n const agentMeta = key ? getMetadata(key) : undefined;\n const target = agentMeta ? getTarget(agentMeta) : undefined;\n const state = key ? this.get(key) : this.get();\n\n if (this.stateChangeListeners.length > 0) {\n this.log('Notifying state change listeners in source');\n this.stateChangeListeners.forEach(listener => {\n listener(state, changes, agentMeta || undefined);\n });\n }\n\n if (key && target) {\n this.instanceLog('Notifying of state change.', key);\n this.post({ action: 'stateUpdate', payload: { state: changes } }, target);\n } else {\n console.log('Notifying everyone');\n // for every key of this.instances, post the state update to the corresponding key\n this.instances.forEach((_, key) => {\n this.post({ action: 'stateUpdate', payload: { state: changes } }, key);\n });\n }\n }\n\n public get(): DerivedState<TConfig>;\n public get(key: string): DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>;\n public get(key?: string): (DerivedCommonState<TConfig> | DerivedState<TConfig>) {\n if (!key) {\n return { ...this.commonState, ...{} as DerivedInstanceState<TConfig> };\n }\n return { ...this.commonState, ...this.instances.get(key) };\n }\n\n public findInstance(context: PorterContext, location: AgentLocation): string | null {\n // Todo: This feels like too-tight coupleing between porter and crann. Should be a better way.\n const searchKey = getKey({ context, ...location })\n for (const [key, instance] of this.instances) {\n if (key === searchKey) {\n this.log('Found instance for key: ', key);\n return key\n }\n }\n this.log('Could not find instance for context and location: ', context, location);\n return null;\n }\n\n public async set(state: Partial<DerivedCommonState<TConfig>>): Promise<void>\n public async set(state: Partial<DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>>, key: string): Promise<void>\n public async set(state: Partial<DerivedInstanceState<TConfig> | DerivedCommonState<TConfig>>, key?: string): Promise<void> {\n const instance = {} as Partial<DerivedInstanceState<TConfig>>;\n const worker = {} as Partial<DerivedCommonState<TConfig>>;\n\n for (const itemKey in state) {\n const item = this.config[itemKey as keyof TConfig] as ConfigItem<any>;\n if (item.partition === 'instance') {\n const instanceItemKey = itemKey as keyof DerivedInstanceState<TConfig>;\n const instanceState = state as Partial<DerivedInstanceState<TConfig>>;\n instance[instanceItemKey] = instanceState[instanceItemKey];\n } else if (!item.partition || item.partition === Partition.Common) {\n const commonItemKey = itemKey as keyof DerivedCommonState<TConfig>;\n const commonState = state as Partial<DerivedCommonState<TConfig>>;\n worker[commonItemKey] = commonState[commonItemKey]!;\n }\n }\n if (key && Object.keys(instance).length > 0) {\n this.instanceLog('Setting instance state: ', key, instance);\n this.setInstanceState(key, instance);\n }\n if (Object.keys(worker).length > 0) {\n this.log('Setting common state: ', worker);\n this.setCommonState(worker);\n }\n }\n\n private async hydrate(): Promise<void> {\n this.log('Hydrating state from storage.');\n const local = await browser.storage.local.get(null);\n const session = await browser.storage.session.get(null);\n const combined = { ...local, ...session };\n const update: Partial<DerivedCommonState<TConfig>> = {}; // Cast update as Partial<DerivedState<TConfig>>\n let hadItems = false;\n for (const prefixedKey in combined) {\n const key = this.removePrefix(prefixedKey);\n if (this.config.hasOwnProperty(key)) {\n const value = combined[key];\n update[key as keyof DerivedCommonState<TConfig>] = value;\n hadItems = true;\n }\n }\n if (hadItems) {\n this.log('Hydrated some items.');\n } else {\n this.log('No items found in storage.');\n }\n this.commonState = { ...this.defaultCommonState, ...update };\n }\n\n private removePrefix(key: string): string {\n if (key.startsWith(this.storagePrefix)) {\n return key.replace(this.storagePrefix, '');\n }\n return key;\n }\n\n private initializeInstanceDefault(): DerivedInstanceState<TConfig> {\n const instanceState: any = {};\n Object.keys(this.config).forEach(key => {\n const item: ConfigItem<any> = this.config[key];\n if (item.partition === 'instance') {\n instanceState[key] = item.default;\n }\n });\n return instanceState;\n }\n\n private initializeCommonDefault(): DerivedCommonState<TConfig> {\n const commonState: any = {};\n Object.keys(this.config).forEach(key => {\n const item: ConfigItem<any> = this.config[key];\n if (item.partition === Partition.Common) {\n commonState[key] = item.default;\n }\n });\n return commonState;\n }\n\n private log(message: string, ...args: any[]) {\n console.log(`CrannSource [core], ` + message, ...args);\n }\n private instanceLog(message: string, key: string, ...args: any[]) {\n console.log(`CrannSource [${key}], ` + message, ...args);\n }\n private error(message: string, ...args: any[]) {\n console.error(`CrannSource [core], ` + message, ...args);\n }\n private warn(message: string, ...args: any[]) {\n console.warn(`CrannSource [core], ` + message, ...args);\n }\n}\n\n\nexport function create<TConfig extends Record<string, ConfigItem<any>>>(config: TConfig, storagePrefix?: string): [\n (key?: string) => (DerivedState<TConfig>),\n (state: Partial<DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>>, key?: string) => Promise<void>,\n (listener: (state: (DerivedInstanceState<TConfig> | DerivedState<TConfig>), changes: Partial<DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>>, agent?: AgentMetadata) => void) => void,\n (context: PorterContext, location: AgentLocation) => string | null,\n] {\n const instance = Crann.getInstance(config, storagePrefix);\n return [\n instance.get.bind(instance),\n instance.set.bind(instance),\n instance.subscribe.bind(instance),\n instance.findInstance.bind(instance),\n ];\n}\n\n\n", "import browser from 'webextension-polyfill';\n\nexport const Partition = {\n Instance: 'instance' as const,\n Common: 'common' as const\n};\nexport const Persistence = {\n Session: 'session' as const,\n Local: 'local' as const,\n None: 'none' as const\n};\n\ntype ConfigItem<T> = {\n default: T;\n partition?: typeof Partition[keyof typeof Partition]\n persist?: typeof Persistence[keyof typeof Persistence]\n}\n\ntype AnyConfig = Record<string, ConfigItem<any>>;\n\n// export type Config = typeof StateConfig;\n\ntype DerivedState<T extends AnyConfig> = {\n [P in keyof T]: T[P]['default'];\n};\ntype DerivedInstanceState<T extends AnyConfig> = {\n [P in keyof T as T[P]['partition'] extends 'instance' ? P : never]: T[P]['default'];\n};\ntype DerivedCommonState<T extends AnyConfig> = {\n [P in keyof T as T[P]['partition'] extends 'common' ? P : never]: T[P]['default'];\n};\n\ntype StateSubscriber<TConfig extends AnyConfig> = {\n keys?: Array<keyof DerivedState<TConfig>>;\n callback: (changes: StateUpdate<TConfig>) => void;\n}\n\ntype CrannAgent<TConfig extends AnyConfig> = {\n get: () => DerivedState<TConfig>;\n set: (update: StateUpdate<TConfig>) => void;\n subscribe: (callback: (changes: StateUpdate<TConfig>) => void, keys?: Array<keyof TConfig>) => () => void;\n}\n\ntype UseCrann<TConfig extends AnyConfig> = <K extends keyof DerivedState<TConfig>>(\n key: K\n) => [\n DerivedState<TConfig>[K],\n (value: DerivedState<TConfig>[K]) => void,\n (callback: (value: DerivedState<TConfig>[K]) => void) => () => void\n ];\n\ntype ConnectReturn<TConfig extends AnyConfig> = [\n UseCrann<TConfig>,\n CrannAgent<TConfig>['get'],\n CrannAgent<TConfig>['set'],\n CrannAgent<TConfig>['subscribe']\n];\n\ntype AgentSubscription<TConfig extends AnyConfig> = {\n (callback: (changes: StateUpdate<TConfig>) => void, key?: keyof DerivedState<TConfig>): number;\n}\n\ntype StateUpdate<TConfig extends AnyConfig> = Partial<DerivedState<TConfig>>;\n\nexport { AnyConfig, ConfigItem, DerivedState, DerivedInstanceState, DerivedCommonState, StateSubscriber, CrannAgent, AgentSubscription, StateUpdate, DerivedState as State, ConnectReturn, UseCrann };\n", "export function deepEqual(a: any, b: any): boolean {\n if (a === b) return true;\n\n if (a == null || typeof a !== 'object' || b == null || typeof b !== 'object') return false;\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n\n if (keysA.length !== keysB.length) return false;\n\n keysA.sort();\n keysB.sort();\n\n for (let i = 0; i < keysA.length; i++) {\n const key = keysA[i];\n if (key !== keysB[i] || !deepEqual(a[key], b[key])) return false;\n }\n return true;\n}", "import { match } from \"assert\";\nimport { ConfigItem, CrannAgent, DerivedState, StateSubscriber, DerivedInstanceState, DerivedCommonState, ConnectReturn, UseCrann } from \"./model/crann.model\";\nimport { connect as connectPorter, PorterContext } from 'porter-source'\n\nlet crannInstance: unknown = null;\n\nexport function connect<TConfig extends Record<string, ConfigItem<any>>>(config: TConfig, context?: string): ConnectReturn<TConfig> {\n let _myKey = 'unset';\n log('Initializing with context: ', context);\n if (crannInstance) {\n log('We had an instance already, returning');\n return crannInstance as ConnectReturn<TConfig>;\n }\n log('No existing instance, creating a new one');\n const [post, setMessages] = connectPorter({ namespace: 'crann', agentContext: context as PorterContext });\n setMessages({\n initialState: (message) => {\n _state = message.payload.state;\n _myKey = message.payload.key;\n listeners.forEach(listener => {\n listener.callback(_state);\n });\n log(`Initial state received and ${listeners.size} listeners notified`);\n },\n stateUpdate: (message) => {\n log('State updated: ', message);\n changes = message.payload.state;\n _state = { ..._state, ...changes };\n if (!!changes) {\n log('Notifying listeners of state update');\n listeners.forEach(listener => {\n if (listener.keys === undefined) {\n log('Found a universal listener, notifying');\n listener.callback(changes!);\n } else {\n const matchFound = listener.keys.some(key => changes!.hasOwnProperty(key));\n if (matchFound) {\n log('Found a specific listener for this item, notifying');\n listener.callback(changes!);\n }\n }\n });\n }\n }\n });\n log('Porter connected. Setting up state and listeners');\n let _state = getDerivedState(config);\n let changes: Partial<DerivedState<TConfig>> | null = null;\n const listeners = new Set<StateSubscriber<TConfig>>();\n\n log('Completed setup, returning instance');\n\n const get = () => _state;\n const set = (newState: Partial<DerivedState<TConfig>>) => {\n console.log('CrannAgent, calling post with setState');\n post({ action: 'setState', payload: { state: newState } });\n }\n const subscribe = (callback: (changes: Partial<DerivedState<TConfig>>) => void, keys?: Array<keyof DerivedState<TConfig>>): () => void => {\n const listener = { keys, callback };\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n }\n }\n\n const useCrann: UseCrann<TConfig> = <K extends keyof DerivedState<TConfig>>(\n key: K\n ) => {\n const getValue = () => get()[key] as (DerivedState<TConfig>);\n const setValue = (value: DerivedState<TConfig>[K]) =>\n set({ [key]: value } as Partial<DerivedState<TConfig>>);\n const subscribeToChanges = (callback: (value: DerivedState<TConfig>[K]) => void) => {\n return subscribe((changes) => {\n if (key in changes) {\n callback(changes[key] as DerivedState<TConfig>[K]);\n }\n }, [key]);\n };\n\n return [getValue(), setValue, subscribeToChanges];\n }\n\n function log(message: string, ...args: any[]) {\n console.log(`CrannAgent [${_myKey}] ` + message, ...args);\n }\n\n const instance: ConnectReturn<TConfig> = [useCrann, get, set, subscribe];\n crannInstance = instance;\n\n return crannInstance as ConnectReturn<TConfig>;\n};\n\nexport function connected(): boolean {\n return crannInstance !== null;\n}\n\nfunction getDerivedState<TConfig extends Record<string, ConfigItem<any>>>(config: TConfig): (DerivedState<TConfig>) {\n const instanceState = {} as DerivedInstanceState<TConfig>;\n\n Object.keys(config).forEach(key => {\n const item: ConfigItem<any> = config[key];\n if (item.partition === 'instance') {\n instanceState[key as keyof DerivedInstanceState<TConfig>] = item.default;\n }\n });\n\n const commonState = {} as DerivedCommonState<TConfig>;\n Object.keys(config).forEach(key => {\n const item: ConfigItem<any> = config[key];\n if (item.partition === 'instance') {\n commonState[key as keyof DerivedCommonState<TConfig>] = item.default;\n }\n });\n\n return { ...instanceState, ...commonState } as unknown as (DerivedState<TConfig>);\n}\n"],
5
+ "mappings": "AAAA,OAAOA,MAAa,wBCEb,IAAMC,EAAY,CACrB,SAAU,WACV,OAAQ,QACZ,EACaC,EAAc,CACvB,QAAS,UACT,MAAO,QACP,KAAM,MACV,EDRA,OAAS,UAAAC,EAAQ,eAAAC,EAAa,UAAAC,EAAQ,aAAAC,MAAiB,gBEFhD,SAASC,EAAUC,EAAQC,EAAiB,CAC/C,GAAID,IAAMC,EAAG,MAAO,GAEpB,GAAID,GAAK,MAAQ,OAAOA,GAAM,UAAYC,GAAK,MAAQ,OAAOA,GAAM,SAAU,MAAO,GAErF,IAAMC,EAAQ,OAAO,KAAKF,CAAC,EACrBG,EAAQ,OAAO,KAAKF,CAAC,EAE3B,GAAIC,EAAM,SAAWC,EAAM,OAAQ,MAAO,GAE1CD,EAAM,KAAK,EACXC,EAAM,KAAK,EAEX,QAAS,EAAI,EAAG,EAAID,EAAM,OAAQ,IAAK,CACnC,IAAME,EAAMF,EAAM,CAAC,EACnB,GAAIE,IAAQD,EAAM,CAAC,GAAK,CAACJ,EAAUC,EAAEI,CAAG,EAAGH,EAAEG,CAAG,CAAC,EAAG,MAAO,EAC/D,CACA,MAAO,EACX,CFZO,IAAMC,EAAN,MAAMA,CAAuD,CAUhE,YAAoBC,EAAiBC,EAAwB,CAAzC,YAAAD,EARpB,KAAQ,UAAwD,IAAI,IAIpE,KAAQ,qBAA8M,CAAC,EACvN,KAAQ,cAAgB,SACxB,KAAQ,KAA6D,IAAM,CAAE,EAGzE,KAAK,IAAI,cAAc,EACvB,KAAK,qBAAuB,KAAK,0BAA0B,EAC3D,KAAK,mBAAqB,KAAK,YAAc,KAAK,wBAAwB,EAC1E,KAAK,QAAQ,EACb,KAAK,IAAI,qBAAqB,EAC9B,GAAM,CAACE,EAAMC,EAAaC,EAAWC,EAAcC,CAAa,EAAIC,EAAO,OAAO,EAClF,KAAK,KAAOL,EACZC,EAAY,CACR,SAAU,CAACK,EAASC,IAAU,CAC1B,GAAI,CAACA,EAAO,CACR,KAAK,IAAI,2CAA2C,EACpD,MACJ,CACA,KAAK,YAAY,kBAAmBA,EAAM,IAAKD,CAAO,EACtD,KAAK,IAAIA,EAAQ,QAAQ,MAAOC,EAAM,GAAG,CAC7C,CACJ,CAAC,EACDH,EAAeG,GAAU,CACrB,KAAK,YAAY,gDAAiDA,EAAM,IAAKA,CAAK,EAClF,IAAMC,EAAY,KAAK,IAAID,EAAM,GAAG,EACpC,KAAK,KAAK,CAAE,OAAQ,eAAgB,QAAS,CAAE,MAAOC,EAAW,IAAKD,EAAM,GAAI,CAAE,EAAGA,EAAM,GAAG,CAClG,CAAC,EACDL,EAAU,CAAC,CAAE,IAAAO,EAAK,eAAAC,EAAgB,QAAAC,EAAS,SAAAC,CAAS,IAAM,CACtD,KAAK,YAAY,2DAA4DH,EAAKC,EAAgBC,EAASC,CAAQ,EACnH,KAAK,YAAYH,CAAG,EACpBN,EAAa,CAAC,CAAE,IAAAM,EAAK,eAAAC,EAAgB,QAAAC,EAAS,SAAAC,CAAS,IAAM,CACzD,KAAK,YAAY,kEAAmEH,EAAKC,EAAgBC,EAASC,CAAQ,EAC1H,KAAK,eAAeH,CAAG,CAC3B,CAAC,CACL,CAAC,EACD,KAAK,cAAgBV,GAAA,KAAAA,EAAiB,KAAK,aAC/C,CAGA,OAAc,YAA6DD,EAAiBC,EAAwC,CAChI,OAAKF,EAAM,SAGP,QAAQ,IAAI,8EAA8E,EAF1FA,EAAM,SAAW,IAAIA,EAAMC,EAAQC,CAAa,EAI7CF,EAAM,QACjB,CAEA,MAAc,YAAYY,EAA4B,CAClD,GAAK,KAAK,UAAU,IAAIA,CAAG,EAKvB,KAAK,YAAY,+DAAgEA,CAAG,MAL1D,CAC1B,KAAK,YAAY,mCAAoCA,CAAG,EACxD,IAAMI,EAAuB,CAAE,GAAG,KAAK,oBAAqB,EAC5D,KAAK,UAAU,IAAIJ,EAAKI,CAAoB,CAChD,CAGJ,CAEA,MAAc,eAAeJ,EAA4B,CACjD,KAAK,UAAU,IAAIA,CAAG,GACtB,KAAK,YAAY,8BAA+BA,CAAG,EACnD,KAAK,UAAU,OAAOA,CAAG,GAEzB,KAAK,YAAY,oDAAqDA,CAAG,CAEjF,CAEA,MAAa,eAAeK,EAA4D,CACpF,KAAK,IAAI,qCAAsCA,CAAK,EACpD,IAAMC,EAAS,CAAE,GAAG,KAAK,YAAa,GAAGD,CAAM,EAC1CE,EAAU,KAAK,YAAaD,CAAM,EAMnC,KAAK,IAAI,sDAAsD,GAL/D,KAAK,IAAI,+GAA+G,EACxH,KAAK,YAAcA,EACnB,MAAM,KAAK,QAAQD,CAAK,EACxB,KAAK,OAAOA,CAAuC,EAI3D,CAEA,MAAa,iBAAiBL,EAAaK,EAA8D,CACrG,KAAK,YAAY,6CAA8CL,EAAKK,CAAK,EACzE,IAAMG,EAAe,KAAK,UAAU,IAAIR,CAAG,GAAK,KAAK,qBAC/CM,EAAS,CAAE,GAAGE,EAAc,GAAGH,CAAM,EACtCE,EAAUC,EAAcF,CAAM,EAK/B,KAAK,YAAY,4DAA6DN,CAAG,GAJjF,KAAK,YAAY,+DAAgEA,CAAG,EACpF,KAAK,UAAU,IAAIA,EAAKM,CAAM,EAC9B,KAAK,OAAOD,EAAyCL,CAAG,EAIhE,CAIA,MAAc,QAAQK,EAA6D,CAC/E,KAAK,IAAI,kBAAkB,EAC3B,IAAII,EAAe,GACnB,QAAWT,KAAQK,GAAS,KAAK,YAAc,CAE3C,IAAMK,EADO,KAAK,OAAOV,CAAG,EACH,SAAW,OAC9BW,EAAQN,EAAQA,EAAML,CAAwC,EAAI,KAAK,YAAYA,CAAG,EAC5F,OAAQU,EAAa,CACjB,IAAK,UACD,MAAME,EAAQ,QAAQ,QAAQ,IAAI,CAAE,CAAC,KAAK,cAAiBZ,CAAc,EAAGW,CAAM,CAAC,EACnFF,EAAe,GACf,MACJ,IAAK,QACD,MAAMG,EAAQ,QAAQ,MAAM,IAAI,CAAE,CAAC,KAAK,cAAiBZ,CAAc,EAAGW,CAAM,CAAC,EACjFF,EAAe,GACf,MACJ,QACI,KACR,CACJ,CACIA,EACA,KAAK,IAAI,qBAAqB,EAE9B,KAAK,IAAI,oBAAoB,CAErC,CAEA,MAAa,OAAuB,CAChC,KAAK,IAAI,gBAAgB,EACzB,KAAK,YAAc,KAAK,mBACxB,KAAK,UAAU,QAAQ,CAACI,EAAGb,IAAQ,CAC/B,KAAK,UAAU,IAAIA,EAAK,KAAK,oBAAoB,CACrD,CAAC,EACD,MAAM,KAAK,QAAQ,EACnB,KAAK,OAAO,CAAC,CAAC,CAClB,CAEO,UAAUc,EAAgM,CAC7M,KAAK,IAAI,sBAAsB,EAC/B,KAAK,qBAAqB,KAAKA,CAAQ,CAC3C,CAIQ,OAAOC,EAAyCf,EAAoB,CACxE,IAAMgB,EAAYhB,EAAMiB,EAAYjB,CAAG,EAAI,OACrCkB,EAASF,EAAYG,EAAUH,CAAS,EAAI,OAC5CX,EAAQL,EAAM,KAAK,IAAIA,CAAG,EAAI,KAAK,IAAI,EAEzC,KAAK,qBAAqB,OAAS,IACnC,KAAK,IAAI,4CAA4C,EACrD,KAAK,qBAAqB,QAAQc,GAAY,CAC1CA,EAAST,EAAOU,EAASC,GAAa,MAAS,CACnD,CAAC,GAGDhB,GAAOkB,GACP,KAAK,YAAY,6BAA8BlB,CAAG,EAClD,KAAK,KAAK,CAAE,OAAQ,cAAe,QAAS,CAAE,MAAOe,CAAQ,CAAE,EAAGG,CAAM,IAExE,QAAQ,IAAI,oBAAoB,EAEhC,KAAK,UAAU,QAAQ,CAACL,EAAGb,IAAQ,CAC/B,KAAK,KAAK,CAAE,OAAQ,cAAe,QAAS,CAAE,MAAOe,CAAQ,CAAE,EAAGf,CAAG,CACzE,CAAC,EAET,CAIO,IAAIA,EAAqE,CAC5E,OAAKA,EAGE,CAAE,GAAG,KAAK,YAAa,GAAG,KAAK,UAAU,IAAIA,CAAG,CAAE,EAF9C,CAAE,GAAG,KAAK,WAAoD,CAG7E,CAEO,aAAaE,EAAwBC,EAAwC,CAEhF,IAAMiB,EAAYC,EAAO,CAAE,QAAAnB,EAAS,GAAGC,CAAS,CAAC,EACjD,OAAW,CAACH,EAAKsB,CAAQ,IAAK,KAAK,UAC/B,GAAItB,IAAQoB,EACR,YAAK,IAAI,2BAA4BpB,CAAG,EACjCA,EAGf,YAAK,IAAI,qDAAsDE,EAASC,CAAQ,EACzE,IACX,CAIA,MAAa,IAAIE,EAA6EL,EAA6B,CACvH,IAAMsB,EAAW,CAAC,EACZC,EAAS,CAAC,EAEhB,QAAWC,KAAWnB,EAAO,CACzB,IAAMoB,EAAO,KAAK,OAAOD,CAAwB,EACjD,GAAIC,EAAK,YAAc,WAAY,CAC/B,IAAMC,EAAkBF,EAClBG,EAAgBtB,EACtBiB,EAASI,CAAe,EAAIC,EAAcD,CAAe,CAC7D,SAAW,CAACD,EAAK,WAAaA,EAAK,YAAcG,EAAU,OAAQ,CAC/D,IAAMC,EAAgBL,EAChBM,EAAczB,EACpBkB,EAAOM,CAAa,EAAIC,EAAYD,CAAa,CACrD,CACJ,CACI7B,GAAO,OAAO,KAAKsB,CAAQ,EAAE,OAAS,IACtC,KAAK,YAAY,2BAA4BtB,EAAKsB,CAAQ,EAC1D,KAAK,iBAAiBtB,EAAKsB,CAAQ,GAEnC,OAAO,KAAKC,CAAM,EAAE,OAAS,IAC7B,KAAK,IAAI,yBAA0BA,CAAM,EACzC,KAAK,eAAeA,CAAM,EAElC,CAEA,MAAc,SAAyB,CACnC,KAAK,IAAI,+BAA+B,EACxC,IAAMQ,EAAQ,MAAMnB,EAAQ,QAAQ,MAAM,IAAI,IAAI,EAC5CoB,EAAU,MAAMpB,EAAQ,QAAQ,QAAQ,IAAI,IAAI,EAChDqB,EAAW,CAAE,GAAGF,EAAO,GAAGC,CAAQ,EAClC1B,EAA+C,CAAC,EAClD4B,EAAW,GACf,QAAWC,KAAeF,EAAU,CAChC,IAAMjC,EAAM,KAAK,aAAamC,CAAW,EACzC,GAAI,KAAK,OAAO,eAAenC,CAAG,EAAG,CACjC,IAAMW,EAAQsB,EAASjC,CAAG,EAC1BM,EAAON,CAAwC,EAAIW,EACnDuB,EAAW,EACf,CACJ,CACIA,EACA,KAAK,IAAI,sBAAsB,EAE/B,KAAK,IAAI,4BAA4B,EAEzC,KAAK,YAAc,CAAE,GAAG,KAAK,mBAAoB,GAAG5B,CAAO,CAC/D,CAEQ,aAAaN,EAAqB,CACtC,OAAIA,EAAI,WAAW,KAAK,aAAa,EAC1BA,EAAI,QAAQ,KAAK,cAAe,EAAE,EAEtCA,CACX,CAEQ,2BAA2D,CAC/D,IAAM2B,EAAqB,CAAC,EAC5B,cAAO,KAAK,KAAK,MAAM,EAAE,QAAQ3B,GAAO,CACpC,IAAMyB,EAAwB,KAAK,OAAOzB,CAAG,EACzCyB,EAAK,YAAc,aACnBE,EAAc3B,CAAG,EAAIyB,EAAK,QAElC,CAAC,EACME,CACX,CAEQ,yBAAuD,CAC3D,IAAMG,EAAmB,CAAC,EAC1B,cAAO,KAAK,KAAK,MAAM,EAAE,QAAQ9B,GAAO,CACpC,IAAMyB,EAAwB,KAAK,OAAOzB,CAAG,EACzCyB,EAAK,YAAcG,EAAU,SAC7BE,EAAY9B,CAAG,EAAIyB,EAAK,QAEhC,CAAC,EACMK,CACX,CAEQ,IAAIjC,KAAoBuC,EAAa,CACzC,QAAQ,IAAI,uBAAyBvC,EAAS,GAAGuC,CAAI,CACzD,CACQ,YAAYvC,EAAiBG,KAAgBoC,EAAa,CAC9D,QAAQ,IAAI,gBAAgBpC,CAAG,MAAQH,EAAS,GAAGuC,CAAI,CAC3D,CACQ,MAAMvC,KAAoBuC,EAAa,CAC3C,QAAQ,MAAM,uBAAyBvC,EAAS,GAAGuC,CAAI,CAC3D,CACQ,KAAKvC,KAAoBuC,EAAa,CAC1C,QAAQ,KAAK,uBAAyBvC,EAAS,GAAGuC,CAAI,CAC1D,CACJ,EA3RahD,EACM,SAA8B,KAD1C,IAAMiD,EAANjD,EA8RA,SAASkD,EAAwDjD,EAAiBC,EAKvF,CACE,IAAMgC,EAAWe,EAAM,YAAYhD,EAAQC,CAAa,EACxD,MAAO,CACHgC,EAAS,IAAI,KAAKA,CAAQ,EAC1BA,EAAS,IAAI,KAAKA,CAAQ,EAC1BA,EAAS,UAAU,KAAKA,CAAQ,EAChCA,EAAS,aAAa,KAAKA,CAAQ,CACvC,CACJ,CG/SA,OAAS,WAAWiB,MAAoC,gBAExD,IAAIC,EAAyB,KAEtB,SAASC,EAAyDC,EAAiBC,EAA0C,CAChI,IAAIC,EAAS,QAEb,GADAC,EAAI,8BAA+BF,CAAO,EACtCH,EACA,OAAAK,EAAI,uCAAuC,EACpCL,EAEXK,EAAI,0CAA0C,EAC9C,GAAM,CAACC,EAAMC,CAAW,EAAIR,EAAc,CAAE,UAAW,QAAS,aAAcI,CAAyB,CAAC,EACxGI,EAAY,CACR,aAAeC,GAAY,CACvBC,EAASD,EAAQ,QAAQ,MACzBJ,EAASI,EAAQ,QAAQ,IACzBE,EAAU,QAAQC,GAAY,CAC1BA,EAAS,SAASF,CAAM,CAC5B,CAAC,EACDJ,EAAI,+BAA+BK,EAAU,IAAI,qBAAqB,CAC1E,EACA,YAAcF,GAAY,CACtBH,EAAI,kBAAmBG,CAAO,EAC9BI,EAAUJ,EAAQ,QAAQ,MAC1BC,EAAS,CAAE,GAAGA,EAAQ,GAAGG,CAAQ,EAC3BA,IACFP,EAAI,qCAAqC,EACzCK,EAAU,QAAQC,GAAY,CACtBA,EAAS,OAAS,QAClBN,EAAI,uCAAuC,EAC3CM,EAAS,SAASC,CAAQ,GAEPD,EAAS,KAAK,KAAKE,GAAOD,EAAS,eAAeC,CAAG,CAAC,IAErER,EAAI,oDAAoD,EACxDM,EAAS,SAASC,CAAQ,EAGtC,CAAC,EAET,CACJ,CAAC,EACDP,EAAI,kDAAkD,EACtD,IAAII,EAASK,EAAgBZ,CAAM,EAC/BU,EAAiD,KAC/CF,EAAY,IAAI,IAEtBL,EAAI,qCAAqC,EAEzC,IAAMU,EAAM,IAAMN,EACZO,EAAOC,GAA6C,CACtD,QAAQ,IAAI,wCAAwC,EACpDX,EAAK,CAAE,OAAQ,WAAY,QAAS,CAAE,MAAOW,CAAS,CAAE,CAAC,CAC7D,EACMC,EAAY,CAACC,EAA6DC,IAA0D,CACtI,IAAMT,EAAW,CAAE,KAAAS,EAAM,SAAAD,CAAS,EAClC,OAAAT,EAAU,IAAIC,CAAQ,EACf,IAAM,CACTD,EAAU,OAAOC,CAAQ,CAC7B,CACJ,EAEMU,EACFR,GACC,CACD,IAAMS,EAAW,IAAMP,EAAI,EAAEF,CAAG,EAC1BU,EAAYC,GACdR,EAAI,CAAE,CAACH,CAAG,EAAGW,CAAM,CAAmC,EACpDC,EAAsBN,GACjBD,EAAWN,GAAY,CACtBC,KAAOD,GACPO,EAASP,EAAQC,CAAG,CAA6B,CAEzD,EAAG,CAACA,CAAG,CAAC,EAGZ,MAAO,CAACS,EAAS,EAAGC,EAAUE,CAAkB,CACpD,EAEA,SAASpB,EAAIG,KAAoBkB,EAAa,CAC1C,QAAQ,IAAI,eAAetB,CAAM,KAAOI,EAAS,GAAGkB,CAAI,CAC5D,CAGA,OAAA1B,EADyC,CAACqB,EAAUN,EAAKC,EAAKE,CAAS,EAGhElB,CACX,CAEO,SAAS2B,GAAqB,CACjC,OAAO3B,IAAkB,IAC7B,CAEA,SAASc,EAAiEZ,EAA0C,CAChH,IAAM0B,EAAgB,CAAC,EAEvB,OAAO,KAAK1B,CAAM,EAAE,QAAQW,GAAO,CAC/B,IAAMgB,EAAwB3B,EAAOW,CAAG,EACpCgB,EAAK,YAAc,aACnBD,EAAcf,CAA0C,EAAIgB,EAAK,QAEzE,CAAC,EAED,IAAMC,EAAc,CAAC,EACrB,cAAO,KAAK5B,CAAM,EAAE,QAAQW,GAAO,CAC/B,IAAMgB,EAAwB3B,EAAOW,CAAG,EACpCgB,EAAK,YAAc,aACnBC,EAAYjB,CAAwC,EAAIgB,EAAK,QAErE,CAAC,EAEM,CAAE,GAAGD,EAAe,GAAGE,CAAY,CAC9C",
6
+ "names": ["browser", "Partition", "Persistence", "source", "getMetadata", "getKey", "getTarget", "deepEqual", "a", "b", "keysA", "keysB", "key", "_Crann", "config", "storagePrefix", "post", "setMessages", "onConnect", "onDisconnect", "onMessagesSet", "source", "message", "agent", "fullState", "key", "connectionType", "context", "location", "initialInstanceState", "state", "update", "deepEqual", "currentState", "wasPersisted", "persistence", "value", "browser", "_", "listener", "changes", "agentMeta", "getMetadata", "target", "getTarget", "searchKey", "getKey", "instance", "worker", "itemKey", "item", "instanceItemKey", "instanceState", "Partition", "commonItemKey", "commonState", "local", "session", "combined", "hadItems", "prefixedKey", "args", "Crann", "create", "connectPorter", "crannInstance", "connect", "config", "context", "_myKey", "log", "post", "setMessages", "message", "_state", "listeners", "listener", "changes", "key", "getDerivedState", "get", "set", "newState", "subscribe", "callback", "keys", "useCrann", "getValue", "setValue", "value", "subscribeToChanges", "args", "connected", "instanceState", "item", "commonState"]
7
7
  }
@@ -1,6 +1,8 @@
1
1
  import { DerivedInstanceState, DerivedCommonState, ConfigItem, DerivedState } from './model/crann.model';
2
+ import { AgentLocation, AgentMetadata, PorterContext } from 'porter-source';
2
3
  export declare class Crann<TConfig extends Record<string, ConfigItem<any>>> {
3
4
  private config;
5
+ private static instance;
4
6
  private instances;
5
7
  private defaultCommonState;
6
8
  private defaultInstanceState;
@@ -9,21 +11,32 @@ export declare class Crann<TConfig extends Record<string, ConfigItem<any>>> {
9
11
  private storagePrefix;
10
12
  private post;
11
13
  constructor(config: TConfig, storagePrefix?: string);
14
+ static getInstance<TConfig extends Record<string, ConfigItem<any>>>(config: TConfig, storagePrefix?: string): Crann<TConfig>;
12
15
  private addInstance;
13
16
  private removeInstance;
14
17
  setCommonState(state: Partial<DerivedCommonState<TConfig>>): Promise<void>;
15
18
  setInstanceState(key: string, state: Partial<DerivedInstanceState<TConfig>>): Promise<void>;
16
19
  private persist;
17
20
  clear(): Promise<void>;
18
- subscribe(listener: (state: (DerivedInstanceState<TConfig> | DerivedState<TConfig>), changes: Partial<DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>>, key?: string) => void): void;
21
+ subscribe(listener: (state: (DerivedInstanceState<TConfig> | DerivedState<TConfig>), changes: Partial<DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>>, agent?: AgentMetadata) => void): void;
19
22
  private notify;
20
23
  get(): DerivedState<TConfig>;
21
24
  get(key: string): DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>;
25
+ findInstance(context: PorterContext, location: AgentLocation): string | null;
22
26
  set(state: Partial<DerivedCommonState<TConfig>>): Promise<void>;
23
27
  set(state: Partial<DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>>, key: string): Promise<void>;
24
28
  private hydrate;
25
29
  private removePrefix;
26
30
  private initializeInstanceDefault;
27
31
  private initializeCommonDefault;
32
+ private log;
33
+ private instanceLog;
34
+ private error;
35
+ private warn;
28
36
  }
29
- export declare function create<TConfig extends Record<string, ConfigItem<any>>>(config: TConfig, storagePrefix?: string): Crann<TConfig>;
37
+ export declare function create<TConfig extends Record<string, ConfigItem<any>>>(config: TConfig, storagePrefix?: string): [
38
+ (key?: string) => (DerivedState<TConfig>),
39
+ (state: Partial<DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>>, key?: string) => Promise<void>,
40
+ (listener: (state: (DerivedInstanceState<TConfig> | DerivedState<TConfig>), changes: Partial<DerivedInstanceState<TConfig> & DerivedCommonState<TConfig>>, agent?: AgentMetadata) => void) => void,
41
+ (context: PorterContext, location: AgentLocation) => string | null
42
+ ];
@@ -1,2 +1,3 @@
1
- import { ConfigItem, CrannAgent } from "./model/crann.model";
2
- export declare function connect<TConfig extends Record<string, ConfigItem<any>>>(config: TConfig, context?: string): CrannAgent<TConfig>;
1
+ import { ConfigItem, ConnectReturn } from "./model/crann.model";
2
+ export declare function connect<TConfig extends Record<string, ConfigItem<any>>>(config: TConfig, context?: string): ConnectReturn<TConfig>;
3
+ export declare function connected(): boolean;
@@ -1,3 +1,3 @@
1
1
  export { create, Crann } from './crann';
2
- export { connect } from './crannAgent';
3
- export { CrannAgent, StateUpdate, State, Partition, Persistence } from './model/crann.model';
2
+ export { connect, connected } from './crannAgent';
3
+ export { CrannAgent, StateUpdate, State, Partition, Persistence, ConfigItem, DerivedState, } from './model/crann.model';
@@ -12,27 +12,38 @@ type ConfigItem<T> = {
12
12
  partition?: typeof Partition[keyof typeof Partition];
13
13
  persist?: typeof Persistence[keyof typeof Persistence];
14
14
  };
15
- type DerivedState<T extends Record<string, ConfigItem<any>>> = {
15
+ type AnyConfig = Record<string, ConfigItem<any>>;
16
+ type DerivedState<T extends AnyConfig> = {
16
17
  [P in keyof T]: T[P]['default'];
17
18
  };
18
- type DerivedInstanceState<T extends Record<string, ConfigItem<any>>> = {
19
+ type DerivedInstanceState<T extends AnyConfig> = {
19
20
  [P in keyof T as T[P]['partition'] extends 'instance' ? P : never]: T[P]['default'];
20
21
  };
21
- type DerivedCommonState<T extends Record<string, ConfigItem<any>>> = {
22
+ type DerivedCommonState<T extends AnyConfig> = {
22
23
  [P in keyof T as T[P]['partition'] extends 'common' ? P : never]: T[P]['default'];
23
24
  };
24
- type StateSubscriber<TConfig extends Record<string, ConfigItem<any>>> = {
25
+ type StateSubscriber<TConfig extends AnyConfig> = {
25
26
  keys?: Array<keyof DerivedState<TConfig>>;
26
27
  callback: (changes: StateUpdate<TConfig>) => void;
27
28
  };
28
- type CrannAgent<TConfig extends Record<string, ConfigItem<any>>> = {
29
- get: () => DerivedCommonState<TConfig> & DerivedInstanceState<TConfig>;
29
+ type CrannAgent<TConfig extends AnyConfig> = {
30
+ get: () => DerivedState<TConfig>;
30
31
  set: (update: StateUpdate<TConfig>) => void;
31
- subscribe: (callback: (changes: StateUpdate<TConfig>) => void, keys?: Array<keyof TConfig>) => number;
32
- unsubscribe: (id: number) => void;
33
- };
34
- type AgentSubscription<TConfig extends Record<string, ConfigItem<any>>> = {
32
+ subscribe: (callback: (changes: StateUpdate<TConfig>) => void, keys?: Array<keyof TConfig>) => () => void;
33
+ };
34
+ type UseCrann<TConfig extends AnyConfig> = <K extends keyof DerivedState<TConfig>>(key: K) => [
35
+ DerivedState<TConfig>[K],
36
+ (value: DerivedState<TConfig>[K]) => void,
37
+ (callback: (value: DerivedState<TConfig>[K]) => void) => () => void
38
+ ];
39
+ type ConnectReturn<TConfig extends AnyConfig> = [
40
+ UseCrann<TConfig>,
41
+ CrannAgent<TConfig>['get'],
42
+ CrannAgent<TConfig>['set'],
43
+ CrannAgent<TConfig>['subscribe']
44
+ ];
45
+ type AgentSubscription<TConfig extends AnyConfig> = {
35
46
  (callback: (changes: StateUpdate<TConfig>) => void, key?: keyof DerivedState<TConfig>): number;
36
47
  };
37
- type StateUpdate<TConfig extends Record<string, ConfigItem<any>>> = Partial<DerivedState<TConfig>>;
38
- export { ConfigItem, DerivedState, DerivedInstanceState, DerivedCommonState, StateSubscriber, CrannAgent, AgentSubscription, StateUpdate, DerivedState as State };
48
+ type StateUpdate<TConfig extends AnyConfig> = Partial<DerivedState<TConfig>>;
49
+ export { AnyConfig, ConfigItem, DerivedState, DerivedInstanceState, DerivedCommonState, StateSubscriber, CrannAgent, AgentSubscription, StateUpdate, DerivedState as State, ConnectReturn, UseCrann };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crann",
3
- "version": "1.0.9",
3
+ "version": "1.0.19",
4
4
  "description": "",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -30,6 +30,9 @@
30
30
  ],
31
31
  "author": "Marc O'Cleirigh",
32
32
  "license": "ISC",
33
+ "dependencies": {
34
+ "porter-source": "^1.0.22"
35
+ },
33
36
  "devDependencies": {
34
37
  "@types/jest": "^29.5.12",
35
38
  "@types/node": "^22.2.0",
@@ -48,7 +51,7 @@
48
51
  "url": "https://github.com/moclei/crann/issues"
49
52
  },
50
53
  "homepage": "https://github.com/moclei/crann#readme",
51
- "dependencies": {
52
- "porter-source": "^1.0.13"
54
+ "peerDependencies": {
55
+ "porter-source": "^1.0.22"
53
56
  }
54
57
  }