From fc40a5c11c2e57fc58f20c8aed7a68eb882bc223 Mon Sep 17 00:00:00 2001
From: James Arthur
- Local-first software platform for developing web and mobile apps with instant reactivity, realtime multi-user collaboration and conflict-free offline. From the inventors of CRDTs. Based on standard open source Postgres and SQLite. -
+ Local-first sync layer for web and mobile apps. Build reactive, realtime, local-first apps directly on Postgres. +-# Quick links +# ElectricSQL + +Sync for modern apps. From the inventors of CRDTs. + +## Quick links +- [Website](https://electric-sql.com) - [Documentation](https://electric-sql.com/docs) -- [Technical intro](https://electric-sql.com/docs/overview/technical-intro) -- [Quickstart](https://electric-sql.com/docs/usage/quickstart) -- [Examples](https://github.com/electric-sql/examples) -- [FAQs](https://electric-sql.com/docs/overview/faqs) +- [Introduction](https://electric-sql.com/docs/intro/local-first) +- [Quickstart](https://electric-sql.com/docs/quickstart) -# What is ElectricSQL? +## What is ElectricSQL? ElectricSQL is a local-first software platform that makes it easy to develop high-quality, modern apps with instant reactivity, realtime multi-user collaboration and conflict-free offline support. [Local-first](https://www.inkandswitch.com/local-first/) is a new development paradigm where your app code talks directly to an embedded local database and data syncs in the background via active-active database replication. Because the app code talks directly to a local database, apps feel instant. Because data syncs in the background via active-active replication it naturally supports multi-user collaboration and conflict-free offline. -# How do I use it? +## How do I use it? ElectricSQL gives you instant local-first for your Postgres. Think of it like "Hasura for local-first". Drop ElectricSQL onto an existing Postgres-based system and you get instant local-first data synced into your apps. -ElectricSQL then provides a whole developer experience for you to control what data syncs where and to work with it locally in your app code. See the [Documentation](https://electric-sql.com/docs) and the [Quickstart guide](https://electric-sql.com/docs/usage/quickstart) to get started. +ElectricSQL then provides a whole developer experience for you to control what data syncs where and to work with it locally in your app code. See the [Introduction](https://electric-sql.com/docs/intro-local-first) and the [Quickstart guide](https://electric-sql.com/docs/quickstart) to get started. -# Repo structure +## Repo structure This is the main repository for the ElectricSQL source code. Key components include: @@ -69,21 +72,21 @@ This is the main repository for the ElectricSQL source code. Key components incl See the Makefiles for test and build instructions and the [e2e](https://github.com/electric-sql/electric/tree/main/e2e) folder for integration tests. -# Team +## Team ElectricSQL was founded by [@thruflo](https://github.com/thruflo) and [@balegas](https://github.com/balegas), under the guidance of: -- [Marc Shapiro](https://lip6.fr/Marc.Shapiro) and [Nuno Preguiça](https://asc.di.fct.unl.pt/~nmp), the co-inventors of CRDTs +- [Marc Shapiro](https://lip6.fr/Marc.Shapiro) and [Nuno Preguiça](https://asc.di.fct.unl.pt/~nmp), two of the co-inventors of CRDTs - [@bieniusa](https://linkedin.com/in/annette-bieniusa-b0807b145), the lead developer of [AntidoteDB](https://www.antidotedb.eu) - [@josevalim](https://www.linkedin.com/in/josevalim), the creator of the [Elixir](https://elixir-lang.org) programming language See the [Team](https://electric-sql.com/about/team) and [Literature](https://electric-sql.com/docs/reference/literature) pages for more details. -# Contributing +## Contributing See the [Community Guidelines](https://github.com/electric-sql/electric/blob/main/CODE_OF_CONDUCT.md) including the [Guide to Contributing](https://github.com/electric-sql/electric/blob/main/CONTRIBUTING.md) and [Contributor License Agreement](https://github.com/electric-sql/electric/blob/main/CLA.md). -# Support +## Support We have an [open community Discord](https://discord.electric-sql.com). Come and say hello and let us know if you have any questions or need any help getting things running. diff --git a/clients/typescript/README.md b/clients/typescript/README.md index a7a2dffe..df9ea6fb 100644 --- a/clients/typescript/README.md +++ b/clients/typescript/README.md @@ -16,14 +16,17 @@ # ElectricSQL Typescript Client -[ElectricSQL](https://electric-sql.com) is a local-first SQL system. It provides active-active cloud sync for embedded SQLite databases and a reactive programming model to bind components to live database queries. +[ElectricSQL](https://electric-sql.com) is a local-first sync layer for modern apps. Use it to build reactive, realtime, local-first apps using standard open source Postgres and SQLite. -The ElectricSQL Typescript Client is the main ElectricSQL client library for developing node, web and JavaScript-based mobile applications. It's designed to work with _any_ SQLite driver or bindings, with convienience functions to integrate with the most popular ones, including the primary drivers for [Expo](https://electric-sql.com/docs/usage/drivers#expo), [React Native](https://electric-sql.com/docs/usage/drivers#react-native), [SQL.js](https://electric-sql.com/docs/usage/drivers#web) and [Node.js](https://electric-sql.com/docs/usage/drivers#edge). +The ElectricSQL Typescript Client is the main ElectricSQL client library. It provides a type-safe database client autogenerated from your database schema and APIs for [Shape-based sync](https://electric-sql.com/docs/usage/data-access/shapes) and [live queries](https://electric-sql.com/docs/usage/data-access/queries) as well as SQLite driver adapters and frontend framework integrations. See the: - [Documentation](https://electric-sql.com/docs) -- [Quickstart](https://electric-sql.com/docs/usage/quickstart) +- [Introduction](https://electric-sql.com/docs/intro/local-first) +- [Quickstart](https://electric-sql.com/docs/quickstart) + +And see the [Drivers documentation](https://electric-sql.com/docs/integrations/drivers) for information on how to use for web, mobile, and server applications including wa-sqlite, React Native, Expo and NodeJS. ## Install @@ -41,100 +44,7 @@ npm install --save electric-sql ## Usage -Instantiate and use your SQLite driver as normal and call `electrify` when opening a new database connection. For example using `react-native-sqlite-storage`: - -```ts -import { electrify } from 'electric-sql/react-native' - -// Import your SQLite driver -import SQLite from 'react-native-sqlite-storage' -SQLite.enablePromise(true) - -// Import your app config and migrations -import config from '.electric/@config' - -// Open an SQLite database connection -const original = await SQLite.openDatabase('example.db') - -// ⚡ Electrify it -const db = electrify(original, config) - -// Use as normal, e.g.: -db.transaction((tx) => tx.executeSql('SELECT 1')) -``` - -### Using in the web browser - -Electric uses [SQL.js](https://electric-sql.com/docs/usage/drivers#web) in the browser with [absurd-sql](https://electric-sql.com/docs/usage/drivers#web) for persistence. This runs in a web worker (which we also use to keep background replication off the main thread). As a result, the electrified db client provides an asynchronous version of a subset of the SQL.js driver interface. - -First create a `worker.js` file that imports and starts an `ElectricWorker` process: - -```ts -import { ElectricWorker } from 'electric-sql/browser' - -ElectricWorker.start(self) -``` - -Then, in your main application: - -```ts -import { initElectricSqlJs } from 'electric-sql/browser' - -// Import your app config and migrations -import config from '.electric/@config' - -// Start the background worker -const url = new URL('./worker.js', import.meta.url) -const worker = new Worker(url, { type: 'module' }) - -// Electrify the SQL.js / absurd-sql machinery -const SQL = await initElectricSqlJs(worker, { - locateFile: (file) => `/${file}`, -}) - -// Open a named database connection -const db = await SQL.openDatabase('example.db', config) -``` - -This gives you persistent, local-first SQL with active-active replication -in your web browser 🤯. Use the db client as normal, with the proviso that -the methods are now async (they return promises rather than direct values). - -See the [Quickstart](https://electric-sql.com/docs/usage/quickstart) guide for more information. - -### Reactivity - -Once electrified, you can bind live database queries to your reactive components, so they automatically update when data changes or comes in over the replication stream. For example: - -```tsx -import React from 'react' -import { Pressable, Text, View } from 'react-native' - -import { useElectric, useElectricQuery } from 'electric-sql/react' - -export const LiveComponent = () => { - const db = useElectric() - const { results } = useElectricQuery('SELECT value FROM items') - - const addItem = () => { - sql.execute('INSERT INTO items VALUES(?)', [crypto.randomUUID()]) - } - - return ( -
yzI8kC9-Rq-#I9O1=U%TXi ztFK9;XpgprzDrw1tDh@6WAV=TU6u;o8Ixj*SN5p!)zsQ>e4`(}{Lh*)>2a+F6<>*I zf#0**@!@hbrClq}NxP!GM*9mj|A6l6=IbLnI*lIRn|cKPktM(|nIrd`Dck?{i}}ec zqnsD=&Hpjqw-CP$b;S*qTRw-iI6^}&oK^Kod%9ZR7nMnCQ0wI!?K8=#`sqc%xan*u zAxn96Z@=WU?a5b_V|wGwu1~^(mV+shkR$!z@x$$T(P*#UTY70|XdF3Y+uYtM%6lq! zs&7^+hDC!nK2>VIE;3KWOy>g4lkO9oIi*1>kAo3!!%Fq$&`Iv@gMg zwUjvh`XpUvkcWeK?QIY=w}3ePzmqiGfIJZU=i0md@(;1*^O>(iFm`cxHMpmQp|rPj zlA+4wo4J=1LzeO$ewmnWZt}pc$LN7|?j_kEl )g?U(1fYURVP5#K)EwbY_axAZ{13eVd)m2Gf<&Sm54VIA3slCitWUGh%Yq8 zpx6E!arMs%uMVg%VQa6m=cY&Uq1VZN*eU!L@x=^t=^L1#Zi|UJG6JHmb*nse8_`TC zXx1OmZ9xUlxmkafF3&at1(}5&x*}Tvbj~b<#Uf>a55;gevE%3nU|T;Svqq%?j6Eui z#%gN0oDpd_wk^+m^~fM-!x_%_DnhTRhw0tR7h$hFe@`&=(9}_FaknhbNFy_S)n^F~ zuWc^k8dL^$;AH5(e~eLBFX}kr1~d<-@JMJt9c!xt&GzT2wkg0;zSxKA{m8ZmOSkob zZ6j@|whD0nakbl7+z6!xv0caO=<2lH;6Vvi^hD|3mK7EB#Mq9vHq5n=( H7T){Z`$uU@u!}%qzxO60Du~hq#fF7oR1|En5M#SoG4^gS_E-`lDx$%P zdJ`2<5EYFSQPEg17C>yVVZj3LJ!h5~W~(d Z zrBXp>2Svy~6bfgBLgC<``rgP|p;%A%936|EH&7^&swxzX=r{BVy`j2KC!L4m$Nz-B z65!tr?yX$(KG*l2c;BhXzY!I=349jsmGs=gsWF;4Hpa{u(-5(K-8 +Xwj^(@SC1J{4qRas8Eg{O9UTh($dhvsfk)0-t8J7{ (~H8#@b`b(%M+GsRqKnwiSLeVcb|078W9E^CsyX`8e?M zX^3r?lsJXTNT!lXC7pCRPi4l6+QK`F7lq^P+qdZPX?LwUx(3$8F;v0{DktgPU#O%| zIYaNCr80I-9pRm=TcVJXd>W3QsP_+J>C&?Cm4r9`UaatJU`WU)y*}@5NhhovnrFp6AXyegZm=}iRyz5y z7WOP78{OXp@4g2AJ;E1o}pj)MpGqv GsT z;L7DorF&)3&n&BVP+iPhYKxtRt0DD @0SRj{V-=V@70)y&Fvi9zY5!&F{vuF5}_LtJg z=KXQe54CXhjXAE9TrRxts$n1;x2Z6WeMhTFasvFj3i>mMn>TO3qjgK^SxK>kPP4;R zl9x&48WrC6MGV*9nj?+uhq!bb?2507&3kKL!;Ttg dB;?GlZLgzHLpcovT}M_TLl#O#ubF?fCMY`S3c>XIwo8^6F-u&>S)*;s5h zUXbB9aM4 T7=1 w=}o0VXzpQf?Ec0b$fJT64Ly?A_@VfJd3A0XB@dEx|VAK>=z_H-{XhEa2E z1z#bl$X{T;Lym#{5Bc~M+hQV(7g0W+moHr`wL#3ToodhIztg-9TUAr?SL7JjZ;|8B zY>RURl`&$notm8O(LV|p*uM(y8>se7Q?B@ZK&U;A7FH54953>lG;xUiA+!aJ1@^~y zuBN{+!=RB4k{sUqjwU1s7%pUFl-Sqz`;6?J=<0~cAwP>SFb)-lN`fD#)fOaM92Y4s z%@8=~BMAfBq!}6K1q`!iPM7YB`o8PT{$~*Ry<->E#sZoLYmV3;D%~0}*Q~HV#}e@m z&5`)j9LLBHOn6{{xNJ-8IAaYjvJb_&y}jI#ot1@0j~)tsm3aSsp1XJHO!6OL+mBl% zT<=pp%VOK)#f#_Y{aHYTVHg)W25G4&@5N8h*S_Hk7huZdunKDf# zU;p5~f_#0v!2ipA=1GX6RScj~9z2P@ame?%AJ{f7iEV1Ao{D_dy-O#o`);jCyjex! z=RVl5{s%;F-GbKdG=eX-XTk^Y+Aj;?jAKHO1yM1%s*Yax5V`I}3vc_Wx7%fqyqY z+_`f{ur-(uGPOP4_xsFc?%2Lfu-(&AQjD_O-#1o$@ {oz-$PzcX|6fe7)<`h+-0_i zK2!~-iLbr&hIrE=&hoYff0~nTg@w!N5a(6w$MgD`OtC2O$hvh4{e$|b=~x 1V$Xemv%g#6I$^#^oD=iD68B5j%>NeaU?K3pCo-zQhvKC2`iF-MkvK5+KTrR1 zs$AwXj}w35L1IqXAd~o6IcLngG3#)vNC(z~`$6oBT%j*iI!q#tRgRzIXpC8Y?Ao~l zyhh1?XX wBIdp{DuO< z_WSp1*Qnv=FKlB3_Uch4eQ=2IGdG-`XNl-zRWO6_27g|9yqbEoCA;py{rg%vTp&)x zuTy)Ib%-VW2QMqpf7pj`bE8}Vn$MaTO#QxU 4v!2 zSlqmE!(d_VOy9w4QpnKHOEr$1996iZj@#fY@uwoMF2;RSu^%%e)0#ApVsac0*3_MC z&bt&}GUdq0$wAKmf1}sckcsv<{=2yl6HsyT>N<#e@ER3Tvc()z!k!d)4Hy32Lzm zV|8JBFY#yseA;N|P-(W8{QIs9rNmz|hb`*B`9<=+5cNpRvJ!Hj Ypg#|4GY%Q#DuwZ z(3A2BH1YFy=Vnd8`<0l}CE_ynykgl>tvobc^7!7~t5!lzS1QFXVkz$S-GQo@&TGy% zd-(URE5y1qbLRBb#rM5vJ!d;j*TL%#^WSS%uQFJ|zg|H5f~(Fp(v-c2@W ^rUQ@{MT9Pjj?c%uBCp=<67j#0ZfHx}Z+ z?4vpBh*$DFOi4-BTJs+~cz}WZgG(&W5SDj6<9*EU(|QOd+hW95b_iQ!i#gxb#A1qB zu8ygWh}f#wMEQ{OzpshbGTT|Kmw8dnt&$&ww9fo@?;eUdw |~rI~GWMM7f5~&5%a>L-87=%7NrHDm~vEOSh`WVOa-`IR{c+s%D<&ix)2hpIFq> zlr!(U-o6bmW*)_CX)V%>&2ijTjG1zbPF$mk_2B(Nm1D4w@_snqRTB^U?++h7ly?I% zuU;t=ZY5~A1Lc=mA&D?>ekjL#SqDu$SO;Bth;iui)DNl8%`uhiZ_W9@Ik|IZ&jRN~ z$q(kNjm;0zY@4WaN-16~kCBRch%sX^cUIDY^x?h``$N m6zC-NxqgW#``WE#PKBRp{WDwaqg{|uz%>vBig<#S}i|;?NV10@)oz!*hwV( zVw{@ujJXu+p&qjq`$N=2h~pRag*YczhZ7V_9!B}nY#++|+|#WU_W%5oknhZW!E@V` z9N3|hQxpHDlB9!}D=q4Al5|nci%L4@g`@{z7Gv qGJJzV8 zz7X};pQ%I-viUT12 Y+TZy92Yq|@EH!?)u52T?xHN`4*%dw=8lVIHXP^)5!6PZJ zXX@IT2;W{EtK+I5GUWqoJ8zBM^#6i`G*%9gy&6Y__q
Xb;3sY-#| *sdaTbL*lx`(xzXgWV;9&!JbO!v|NNO!( ziI=jLg_BzLfq@3lUk@Ir!9YFfLK$oM65_>j=Eo7lO9xm{e !CW0p{uf5PR>4SK zLH-^i{Ct!x3?w%y8L6@C#Q~HP3i^gPgu`VA`cbNPvIG1m-xh%TYJ#qkzCMo7Sw;V1 z4Sk~o;kqF%Tte4(Y~O{HihSsH4Q*>G!gYsz7djlu1%^7%ucqa$M?5^h{W|DQ4J~&A z%;!7o%Yp8mggrfBKF_fv6XE(!iNy|%v7YZmx z@(_-jQ)V*Mdx9O{OV5zdH^dWeRyln04~8U$c=;gIbDYpoNv_}RBu#|9`7BWgGwvf+ zL h(8^1k|mQHl#srbx-)~mE{?W+ox|QO zu&;n}yol7imlY98I$z3M8A1H<5cA_mMv|D3vVyTbj{ezd$K9C@<=fc)K2zag+4DCk zFGxy-`Wh590FX8(#I8u)_3z1{><#c*WdA61rXh(Gq|>iUQ3ZPMDffvx$|@(nOl 6RN+E$g#S4A=ZP zo@U9gy_u*Tq<3p}^l#zy#R#=x@-N9_V_6}labTV9WX9N)yjz>ql8efm{{n=1?coFH zM=CTvl&MAi4{IBb^}LNi-KvK7^Ocs-GQM&LUf_oxT;q<;2J`UdZL6NOK3P0y)V-s# zE8g$FPN^naTZR`6^07Z#Ec8D-W^d1mrZ`h_YUzlb yzI8kC9-Rq-#I9O1=U%TXi ztFK9;XpgprzDrw1tDh@6WAV=TU6u;o8Ixj*SN5p!)zsQ>e4`(}{Lh*)>2a+F6<>*I zf#0**@!@hbrClq}NxP!GM*9mj|A6l6=IbLnI*lIRn|cKPktM(|nIrd`Dck?{i}}ec zqnsD=&Hpjqw-CP$b;S*qTRw-iI6^}&oK^Kod%9ZR7nMnCQ0wI!?K8=#`sqc%xan*u zAxn96Z@=WU?a5b_V|wGwu1~^(mV+shkR$!z@x$$T(P*#UTY70|XdF3Y+uYtM%6lq! zs&7^+hDC!nK2>VIE;3KWOy>g4lkO9oIi*1>kAo3!!%Fq$&`Iv@gMg zwUjvh`XpUvkcWeK?QIY=w}3ePzmqiGfIJZU=i0md@(;1*^O>(iFm`cxHMpmQp|rPj zlA+4wo4J=1LzeO$ewmnWZt}pc$LN7|?j_kEl )g?U(1fYURVP5#K)EwbY_axAZ{13eVd)m2Gf<&Sm54VIA3slCitWUGh%Yq8 zpx6E!arMs%uMVg%VQa6m=cY&Uq1VZN*eU!L@x=^t=^L1#Zi|UJG6JHmb*nse8_`TC zXx1OmZ9xUlxmkafF3&at1(}5&x*}Tvbj~b<#Uf>a55;gevE%3nU|T;Svqq%?j6Eui z#%gN0oDpd_wk^+m^~fM-!x_%_DnhTRhw0tR7h$hFe@`&=(9}_FaknhbNFy_S)n^F~ zuWc^k8dL^$;AH5(e~eLBFX}kr1~d<-@JMJt9c!xt&GzT2wkg0;zSxKA{m8ZmOSkob zZ6j@|whD0nakbl7+z6!xv0caO=<2lH;6Vvi^hD|3mK7EB#Mq9vHq5n=( H7T){Z`$uU@u!}%qzxO60Du~hq#fF7oR1|En5M#SoG4^gS_E-`lDx$%P zdJ`2<5EYFSQPEg17C>yVVZj3LJ!h5~W~(d Z zrBXp>2Svy~6bfgBLgC<``rgP|p;%A%936|EH&7^&swxzX=r{BVy`j2KC!L4m$Nz-B z65!tr?yX$(KG*l2c;BhXzY!I=349jsmGs=gsWF;4Hpa{u(-5(K-8 +Xwj^(@SC1J{4qRas8Eg{O9UTh($dhvsfk)0-t8J7{ (~H8#@b`b(%M+GsRqKnwiSLeVcb|078W9E^CsyX`8e?M zX^3r?lsJXTNT!lXC7pCRPi4l6+QK`F7lq^P+qdZPX?LwUx(3$8F;v0{DktgPU#O%| zIYaNCr80I-9pRm=TcVJXd>W3QsP_+J>C&?Cm4r9`UaatJU`WU)y*}@5NhhovnrFp6AXyegZm=}iRyz5y z7WOP78{OXp@4g2AJ;E1o}pj)MpGqv GsT z;L7DorF&)3&n&BVP+iPhYKxtRt0DD @0SRj{V-=V@70)y&Fvi9zY5!&F{vuF5}_LtJg z=KXQe54CXhjXAE9TrRxts$n1;x2Z6WeMhTFasvFj3i>mMn>TO3qjgK^SxK>kPP4;R zl9x&48WrC6MGV*9nj?+uhq!bb?2507&3kKL!;Ttg dB;?GlZLgzHLpcovT}M_TLl#O#ubF?fCMY`S3c>XIwo8^6F-u&>S)*;s5h zUXbB9aM4 T7=1 w=}o0VXzpQf?Ec0b$fJT64Ly?A_@VfJd3A0XB@dEx|VAK>=z_H-{XhEa2E z1z#bl$X{T;Lym#{5Bc~M+hQV(7g0W+moHr`wL#3ToodhIztg-9TUAr?SL7JjZ;|8B zY>RURl`&$notm8O(LV|p*uM(y8>se7Q?B@ZK&U;A7FH54953>lG;xUiA+!aJ1@^~y zuBN{+!=RB4k{sUqjwU1s7%pUFl-Sqz`;6?J=<0~cAwP>SFb)-lN`fD#)fOaM92Y4s z%@8=~BMAfBq!}6K1q`!iPM7YB`o8PT{$~*Ry<->E#sZoLYmV3;D%~0}*Q~HV#}e@m z&5`)j9LLBHOn6{{xNJ-8IAaYjvJb_&y}jI#ot1@0j~)tsm3aSsp1XJHO!6OL+mBl% zT<=pp%VOK)#f#_Y{aHYTVHg)W25G4&@5N8h*S_Hk7huZdunKDf# zU;p5~f_#0v!2ipA=1GX6RScj~9z2P@ame?%AJ{f7iEV1Ao{D_dy-O#o`);jCyjex! z=RVl5{s%;F-GbKdG=eX-XTk^Y+Aj;?jAKHO1yM1%s*Yax5V`I}3vc_Wx7%fqyqY z+_`f{ur-(uGPOP4_xsFc?%2Lfu-(&AQjD_O-#1o$@ {oz-$PzcX|6fe7)<`h+-0_i zK2!~-iLbr&hIrE=&hoYff0~nTg@w!N5a(6w$MgD`OtC2O$hvh4{e$|b=~x 1V$Xemv%g#6I$^#^oD=iD68B5j%>NeaU?K3pCo-zQhvKC2`iF-MkvK5+KTrR1 zs$AwXj}w35L1IqXAd~o6IcLngG3#)vNC(z~`$6oBT%j*iI!q#tRgRzIXpC8Y?Ao~l zyhh1?XX wBIdp{DuO< z_WSp1*Qnv=FKlB3_Uch4eQ=2IGdG-`XNl-zRWO6_27g|9yqbEoCA;py{rg%vTp&)x zuTy)Ib%-VW2QMqpf7pj`bE8}Vn$MaTO#QxU 4v!2 zSlqmE!(d_VOy9w4QpnKHOEr$1996iZj@#fY@uwoMF2;RSu^%%e)0#ApVsac0*3_MC z&bt&}GUdq0$wAKmf1}sckcsv<{=2yl6HsyT>N<#e@ER3Tvc()z!k!d)4Hy32Lzm zV|8JBFY#yseA;N|P-(W8{QIs9rNmz|hb`*B`9<=+5cNpRvJ!Hj Ypg#|4GY%Q#DuwZ z(3A2BH1YFy=Vnd8`<0l}CE_ynykgl>tvobc^7!7~t5!lzS1QFXVkz$S-GQo@&TGy% zd-(URE5y1qbLRBb#rM5vJ!d;j*TL%#^WSS%uQFJ|zg|H5f~(Fp(v-c2@W ^rUQ@{MT9Pjj?c%uBCp=<67j#0ZfHx}Z+ z?4vpBh*$DFOi4-BTJs+~cz}WZgG(&W5SDj6<9*EU(|QOd+hW95b_iQ!i#gxb#A1qB zu8ygWh}f#wMEQ{OzpshbGTT|Kmw8dnt&$&ww9fo@?;eUdw |~rI~GWMM7f5~&5%a>L-87=%7NrHDm~vEOSh`WVOa-`IR{c+s%D<&ix)2hpIFq> zlr!(U-o6bmW*)_CX)V%>&2ijTjG1zbPF$mk_2B(Nm1D4w@_snqRTB^U?++h7ly?I% zuU;t=ZY5~A1Lc=mA&D?>ekjL#SqDu$SO;Bth;iui)DNl8%`uhiZ_W9@Ik|IZ&jRN~ z$q(kNjm;0zY@4WaN-16~kCBRch%sX^cUIDY^x?h``$N m6zC-NxqgW#``WE#PKBRp{WDwaqg{|uz%>vBig<#S}i|;?NV10@)oz!*hwV( zVw{@ujJXu+p&qjq`$N=2h~pRag*YczhZ7V_9!B}nY#++|+|#WU_W%5oknhZW!E@V` z9N3|hQxpHDlB9!}D=q4Al5|nci%L4@g`@{z7Gv qGJJzV8 zz7X};pQ%I-viUT12 Y+TZy92Yq|@EH!?)u52T?xHN`4*%dw=8lVIHXP^)5!6PZJ zXX@IT2;W{EtK+I5GUWqoJ8zBM^#6i`G*%9gy&6Y__q
Xb;3sY-#| -