Compare commits

...

850 Commits

Author SHA1 Message Date
Thomas Stephens
58d0318f3d
Added audio (#1408)
* Added audio

* Update stream.html

Clean up edit.

* Update stream.html

Somehow my last commit seemed to be a mistake.
2025-07-24 23:58:52 -07:00
ponzS
938697dee9
relay-sqlite-example (#1403)
* relay-sqlite-example

Manager+Adapter

* Update README.md
2025-06-15 01:39:24 -07:00
Mark Nadal
4b43fa7f2c
ci failed cause github made API breaking changes... does this fix? 2025-04-02 09:36:23 -07:00
leo
ff4bf9293c
docs: remove repetitive words (#1392)
Signed-off-by: RiceChuan <lc582041246@gmail.com>
2025-03-08 11:15:39 -08:00
brad winter
0c423c999c
remove redundant return statement (#1394)
* comment out redundant return;

* remove redundant return
2025-03-08 11:13:14 -08:00
Mark Nadal
e3a5a52506
Update README.md 2024-11-29 15:52:33 -08:00
Mark Nadal
9a0e259a9b
Merge branch 'deploys' into master 2024-11-23 17:51:50 -08:00
Mark Nadal
5305f01011 Merge branch 'master' of http://github.com/amark/gun 2024-11-23 17:47:30 -08:00
Mark Nadal
90b88959d0 universal notification system 2024-11-23 17:47:13 -08:00
carlin978
7cc4cce1a3
Update STUN servers (#1381)
Commented out sipgate.net STUN server.
Added Cloudflare STUN server.
2024-07-23 05:56:40 -07:00
Andreas Heissenberger
03735dc09c
fix: ERROR: Radisk needs store.put interface (#1374) 2024-04-15 12:42:03 -07:00
Malcolm Blaney
1c095b13e9
Check atom exists in graph when deciding to read from disk (#1371) 2024-04-04 11:01:15 -07:00
Mark Nadal
faff9efaca Merge branch 'master' of http://github.com/amark/gun 2024-03-11 13:55:50 -07:00
Mark Nadal
7a2767a763 webrtc accept getUserMedia streams as peer 2024-03-11 13:50:17 -07:00
Simardeep Singh
c47800f4d8
update SECURITY.md file and change the versions to 0.2020.x (#1365) 2024-02-06 20:21:18 -08:00
Bradley Matusiak
e584906a65
Loading fix (#1356)
* does this load better

* check window.Gun too in rfs
2024-02-05 03:10:35 -08:00
Bradley Matusiak
3070627c83
... works (#1357) 2024-02-05 03:06:38 -08:00
Simardeep Singh
3bd809818f
Create SECURITY.md (#1364) 2024-02-05 03:05:11 -08:00
mimiza
61df63c96e
Fix SEA certificate verification, allow multiple pubs (#1358) 2024-02-05 03:01:31 -08:00
Bradley Matusiak
7eb6d38cfc
quick-fix (#1355) 2024-01-17 10:47:07 -08:00
Mark Nadal
638c2c3c23 have unbuild function wrap to prevent scope leaks & allow RETURN hehehe so I can reject @bmatusiak 's lS change O:) O:) I love you you're a hero!
later with @bmatusiak check sea.then for '../gun.js' vs '../' vs ...
note: src/index -> core.js
TODO: something about WebRTC candidates hitting ack decrement limits?
2024-01-17 10:13:00 -08:00
Bradley Matusiak
5c52df2eee
react-native detection, and load needed shims (#1349)
* react-native detection

* added lib mobile

* changed back to gun. for another solution
2024-01-17 09:49:52 -08:00
Bradley Matusiak
7cb337c158
add a way to select stats file from url (#1351) 2024-01-17 09:46:48 -08:00
Bradley Matusiak
e07c9b21ec
sea blobs! (#1353)
* sea blobs!

* and null origins

* null fix

* null check is last
2024-01-17 09:45:09 -08:00
Mark Nadal
203bd40932 cleaned up Book results & sorting & caching 2024-01-16 21:08:14 -08:00
Mark Nadal
3688ba1cc6 bump path 2024-01-16 03:40:40 -08:00
Mark Nadal
78a40daf46 RAD & Book promoted! + buggy example: test/rad/book.html 2024-01-16 02:54:07 -08:00
Mark Nadal
d7f19473a4 book & rad APIs stabilizing 2024-01-15 16:46:18 -08:00
Mark Nadal
dc5f90ad61 Merge branch 'master' of http://github.com/amark/gun 2023-12-28 16:07:56 -08:00
Mark Nadal
2b4f750392 is ??? this a MVP of book & rad ???? thanks to @rogowski 2023-12-28 16:07:51 -08:00
Bradley Matusiak
c440a7cc88
update src/index (#1254)
* update src/index

* update

* src/index fix

* added src/core
2023-12-14 18:11:32 -08:00
Jay Byoun
6dfaaf229b
add one click deploy to readme (#1342) 2023-11-25 15:59:35 -08:00
Bradley Matusiak
5ff33b7ac3
WS ws.path fix (#1343)
* Update wire.js

* Update wire.js

* Update wire.js
2023-11-25 15:54:46 -08:00
Mark Nadal
efb2552997 encode objects 2023-11-17 03:44:20 -08:00
Mark Nadal
5d3cbaca19 Merge branch 'master' of http://github.com/amark/gun 2023-11-10 04:37:57 -08:00
Mark Nadal
96b1402a65 yay format change 2023-11-10 04:24:26 -08:00
Mark Nadal
0251de11ec
Update README.md 2023-10-19 11:43:47 -07:00
Anton
1862fb69fd
Remove unused imports (#1337) 2023-10-17 17:53:51 -07:00
Mark Nadal
f882b86889
Thanks Jason Stallings @octalmage !!! 2023-05-26 19:23:47 -07:00
ritchia1
7f6a0b27c3
Support variable number of auth retry attempts through opt.retries (#1325)
Maintain default to 9 to ensure backwards compatibility
2023-05-20 20:47:39 -07:00
ritchia1
0cb9706393
Fix opt.s3.fakes3 parsing issue (#1318)
* Fix opt.s3.fakes3 parsing issue

* Fix second typo within if block
2023-05-12 11:33:40 -07:00
Mark Nadal
4c01db9a67 up mob 2023-04-29 12:19:21 -07:00
Mark Nadal
1304ef90e8 reuse 2023-04-28 15:24:40 -07:00
Mark Nadal
8c4d38e30e oops 2023-04-28 04:13:56 -07:00
Mark Nadal
30eff267ea nope need curl lol 2023-04-28 04:08:51 -07:00
Mark Nadal
580058056f reduce? 2023-04-28 03:35:51 -07:00
Mark Nadal
bfedfc027c sleepy ugh 2023-04-28 02:57:08 -07:00
Mark Nadal
bf1565398b ugh 2023-04-28 02:53:07 -07:00
Mark Nadal
8b1f055cd1 auto update 2023-04-28 02:26:16 -07:00
Mark Nadal
ca0e7e35ae trim 2023-04-26 02:03:32 -07:00
Mark Nadal
e15416ff2d longer 2023-04-26 01:27:48 -07:00
Mark Nadal
0e7121b8f4 finally! 2023-04-26 01:13:17 -07:00
Mark Nadal
2183e6acdf log 2023-04-25 20:02:28 -07:00
Mark Nadal
48b5a0e765 what about now? 2023-04-25 19:41:34 -07:00
Mark Nadal
1ddf797fff reverse? 2023-04-25 15:42:34 -07:00
Mark Nadal
2afde8db4a hmm 2023-04-25 04:18:46 -07:00
Mark Nadal
c6b9fa8d13 back? 2023-04-25 03:44:38 -07:00
Mark Nadal
c9283d8fa6 bump 2023-04-25 03:16:21 -07:00
Mark Nadal
58f9135f6b again 2023-04-25 03:06:35 -07:00
Mark Nadal
b79e5bbad7 test install 2023-04-25 02:29:52 -07:00
Mark Nadal
b14d5da558
Thank you Murage Martin @murageyun for donating!!! 2023-04-07 00:16:19 -07:00
Sharlon Balbalosa
9433e5ca06
fix: change type to string instead of literal newPassword (#1309) 2023-03-13 00:00:21 -07:00
Adriano Rogowski
73b661a6a5 Book passing encode/decode test. 2023-03-02 19:54:30 -03:00
Dimitris Apostolou
9e616ea3a9
Fix typos (#1299) 2023-02-25 17:09:54 -08:00
Mark Nadal
1bdab7394a Book formatting + more tests 2023-02-23 19:07:43 -08:00
Mark Nadal
d732a9e48b fix page size transfer 2023-02-17 19:36:15 -08:00
Mark Nadal
c274977eb6 Merge branch 'master' of http://github.com/amark/gun
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
2023-02-17 18:28:04 -08:00
Mark Nadal
81a29cdd60 fix book mismatch between parse & parseless 2023-02-17 18:26:25 -08:00
Mark Nadal
713380ccd2
opt.env = process.env 2023-02-14 02:03:43 -08:00
Mark Nadal
6f6d8b533d Merge branch 'master' of http://github.com/amark/gun 2023-01-30 15:29:43 -08:00
Mark Nadal
0665787e2b thanks @rogowski book test read/memory/disk variations 2023-01-30 15:29:30 -08:00
Anton
2ee0bed0c1
Allow Dockerfile to auto-update Node version (#1303) 2023-01-24 15:53:52 -08:00
Mark Nadal
9abebd7673 rad fixed dup text, perf, read + tests. 2023-01-24 15:51:26 -08:00
Mark Nadal
8778ca4138 basic book test + some failing ones (cause its so early) 2023-01-19 15:04:56 -08:00
Mark Nadal
eca0451c4e wrapper for browser tests of book @rogowski 2023-01-19 14:35:24 -08:00
Mark Nadal
9ceb86b208 revert tower tying while publishing AXE fix 2022-12-23 01:11:13 -08:00
Mark Nadal
47c070932a revert & make axe not skip if other props 2022-12-21 06:28:20 -08:00
Mark Nadal
73d54b6271 toString must be stringy! 2022-11-30 23:27:51 -08:00
Adriano Rogowski
2d3993125d fix .off() 2022-10-27 20:14:14 -03:00
Martti Malmi
ad8e67e3c3
make SEA work in service worker (no window obj) (#1282) 2022-10-18 22:45:39 -07:00
Mark Nadal
6d7e980b5f silly book. Is this correct? 2022-08-25 14:07:35 -07:00
Mark Nadal
e9439daa51 THANK YOU NORMAN! @nsreed 2022-08-25 14:06:25 -07:00
Norman Reed
94c6a97ae7
Rad book tests (#1274)
* allow unset to unset user nodes

* add options to type declarations for put and set

* roll back unset.js changes

* work toward radisk3/book unit testing

Co-authored-by: Norman Reed <norman.s.reed@gmail.com>
2022-08-25 12:05:54 -07:00
Mark Nadal
f25747443e fix gun <-> axe mismatch on uninit data 2022-08-21 14:06:57 -07:00
Mark Nadal
680f871aa3 PANIC 4: AXE get subscriptions 2022-08-13 14:01:54 -07:00
Mark Nadal
b125d8d150 Merge branch 'master' of http://github.com/amark/gun
chill!
2022-08-13 12:01:32 -07:00
Martti Malmi
7123207c66
Panic tests for rod & radix.js try / catch (#1269)
* try/catch radix.js

* rod test/panic/chat.js

rod test/panic/holy-grail.js

rod panic tests
2022-08-13 11:58:00 -07:00
Mark Nadal
89b24d3862 subscribe only on backpropagation 2022-08-12 18:17:01 -07:00
Mark Nadal
d5c8a02980 test undo tmp 2022-08-10 16:56:05 -07:00
Mark Nadal
451c33a69a reduce not-found acks if via/relay has found ## 2022-08-10 16:21:06 -07:00
Mark Nadal
07b30ed602 unbuild - PUBLISHED! 1238 2022-08-09 15:33:22 -07:00
I001962
d06359f45c
SEA - Update sea.work to support hex (#1266)
This was would encode to hex:
```var data = "hello world";
var hash1 = await SEA.work(data, null, null, {name: "SHA-256",encode:
"hex"});
```
but this would not verify correctly:
gun.get('#').get(hash1).put(data);

This PR will first check base64 (current functionality) and if it fails now it will fall back and check hex.
2022-08-09 15:24:29 -07:00
Mark Nadal
cedf9b8809 stupid cache miss kills me all + update examples 2022-08-09 15:16:58 -07:00
Mark Nadal
e840df43af note/fix 2022-08-03 16:33:16 -07:00
Mark Nadal
ef59ea185c mark fails at JSON again 2022-08-03 14:52:44 -07:00
Mark Nadal
233d2612d1 fix login maybe? AGAIN? 2022-08-03 14:13:17 -07:00
Mark Nadal
cfad98b0c6 it's alive!!!! 2022-08-03 13:59:12 -07:00
Mark Nadal
3dbdd4e43c is this good enough? 2022-08-02 16:45:03 -07:00
Mark Nadal
db99ad65fb argh, axe needed skip reset. + always relay up for now 2022-08-02 16:42:16 -07:00
Mark Nadal
5efedd7308 fix peer sharing 2022-07-31 20:22:20 -07:00
Mark Nadal
8f6322efc0 browser axe remember, random, fallback. 2022-07-31 17:26:14 -07:00
Mark Nadal
7ec5805d8d fix log 2022-07-31 17:14:41 -07:00
Mark Nadal
faff04ff63 floor >> 2022-07-31 15:54:26 -07:00
Mark Nadal
3749bf74cf perf seemed worth it to add MC as sI 2022-07-31 15:53:45 -07:00
Mark Nadal
027edcb54e requery - oye, hopefully safer 2022-07-31 15:51:01 -07:00
Mark Nadal
cdfe87c2b6 fix ws error/reconnect ordering 2022-07-31 15:44:25 -07:00
Joe Thielen
9ca6e59d32
Removed bad dir references (#1263) 2022-07-19 00:26:54 -07:00
Mark Nadal
8b52cbb2cb
oh, try export 2022-07-14 17:27:31 -07:00
Mark Nadal
e4e703c957
Create radisk3.js 2022-07-14 16:54:01 -07:00
Mark Nadal
d6d50040dc
Create book.js 2022-07-14 16:42:17 -07:00
Bradley Matusiak
809eae4f98
dont block stop chain because of deprecated nts (#1252) 2022-07-12 20:57:27 -07:00
Mark Nadal
081931a6d4
Thanks @gabriellemon Gabriel for donating! :) 2022-06-26 04:02:39 -07:00
Mark Nadal
5687bddc90
Thank you @b-lack Gerrit Balindt for sponsoring! 2022-06-18 17:05:06 -07:00
Mark Nadal
ac52811880 put sampling, mob, tests. 2022-06-06 18:20:18 -07:00
Mark Nadal
19cb91ee4e Merge branch 'master' of http://github.com/amark/gun
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
2022-06-06 18:14:10 -07:00
Mark Nadal
46d926e831 put sampling, mob, tests 2022-06-06 18:08:43 -07:00
Bradley Matusiak
9c9a5fd293
Lex builder (#1249)
* lex builder

* updated lex builder

* added args to map

* added match to lex-builder

* update match

* moved matach and gave Lex to Global Gun

* fix match in lex
2022-06-06 15:04:29 -07:00
Bradley Matusiak
87652467b5
chain fork (#1247)
* chain fork

* removed test

* removed unbuild

* moved chain fork to libs folder
2022-06-06 15:02:36 -07:00
Bradley Matusiak
de46cccc1e
Unbuild update (#1248)
* unbuild gun

* unbuild sea
2022-06-03 12:27:38 -07:00
abenezermario
fa1e1578f7
fixed s3 (#1245) 2022-05-30 20:06:43 -07:00
Bradley Matusiak
42720c57ea
unbuild is no longer silent (#1239)
* prepare to sync gun lib unbuild on npm install

* Update unbuild.js

* Update package.json

* Update package.json
2022-05-30 20:02:25 -07:00
Daniel Raeder
8facbcc095
Adds Heroku deploy unit testing thanks to @bmatusiak (#1244)
* Add Heroku deploy unit testing thanks to @bmatusiak

1243.js is a reference to PR #1243

* Update 1243.js

Corrected some indentation, removed unnecessary require
2022-05-30 20:00:14 -07:00
Daniel Raeder
a634b37b1a
Heroku fixes (#1243)
* Update app.json

Removed 'stack' parameter to enable using the latest Heroku stack

* Update package.json

Remove prepare parameter to fix Heroku deploys
2022-05-29 20:00:52 -07:00
Mark Nadal
525d834784 Merge branch 'master' of http://github.com/amark/gun 2022-05-29 19:43:31 -07:00
Mark Nadal
2beb258b4c OK start 2022-05-29 19:43:24 -07:00
Shaun Hamilton
39337338bd
fix(npm): change glob preventing GunDataNode.d.ts (#1241) 2022-05-25 10:25:33 -07:00
Bradley Matusiak
c85cb4365d
webpack example (#1240) 2022-05-24 14:08:18 -07:00
Bradley Matusiak
210a5834c6
prepare to sync gun lib unbuild on npm install (#1237) 2022-05-23 17:14:25 -07:00
Mark Nadal
55682b6f4b bump (ok ack + webrtc) @Draeder yay thanks! 2022-05-18 18:36:35 -07:00
Mark Nadal
7335f8c866 Merge branch 'master' of http://github.com/amark/gun 2022-05-18 18:10:39 -07:00
Mark Nadal
904b2f8e7f ok ack + webrtc 2022-05-18 18:10:36 -07:00
Bradley Matusiak
ced9cde41b
websocket ../index to ./index (#1235)
* Update sea.js

* Update auth.js

* Update sea.js

* Update auth.js

* Update rs3.js

* added test for login via {pub}

* gh page build

* fix index path in websocket plugin
2022-05-15 18:07:35 -07:00
Mark Nadal
94ab05b032 Merge branch 'master' of http://github.com/amark/gun 2022-05-08 20:34:16 -07:00
Mark Nadal
589a7784dc PANIC tests + AXE get dedup + fix DAM add by id 2022-05-08 20:34:06 -07:00
George Shammas
440bbffb73
lib/ison: Use setTimeout instead of setImmediate. Allows the use of indexDB in browsers (#1226) 2022-05-05 13:13:18 -07:00
Bradley Matusiak
e3eaf5e268
Allow {pair.pub} to login with pass (#1233)
* Update sea.js

* Update auth.js

* Update sea.js

* Update auth.js

* Update rs3.js

* added test for login via {pub}

* gh page build
2022-04-29 16:26:26 -07:00
Mark Nadal
a092f5a725 @rococtz fix + @draeder AXE tests. Force. 2022-04-26 23:20:34 -07:00
Mark Nadal
8f79ff7bb0 Merge branch 'master' of http://github.com/amark/gun 2022-04-26 23:18:54 -07:00
Mark Nadal
ebe6f0cf3b @rococtz fix + @dreader AXE tests 2022-04-26 23:12:52 -07:00
George Shammas
9d94b433d0
export SEA type information (#1224) 2022-04-02 16:26:08 -07:00
Mark Nadal
ff3c4f6e69
Thank you @JacobMillner for donating!!! 2022-04-02 10:07:23 -07:00
Mark Nadal
f062fc330b
Thanks Hunter Owens @howens for backing! 2022-03-23 00:28:37 -07:00
Mark Nadal
4f19440262 axe tweaks 2022-03-23 00:10:57 -07:00
Mark Nadal
e4bb977d5d
Thanks @wayjake for https://wallie.io/ supporting GUN! 2022-03-17 21:49:30 -07:00
Thorsten Zoerner
0e8b4549df
Update axe.js (#1222)
Should solve uncaught exception:
```
error] TypeError: Cannot read properties of undefined (reading 'lack')
    at Function.GET.turn (/root/.node-red/node_modules/gun/lib/axe.js:42:20)
    at Timeout._onTimeout (/root/.node-red/node_modules/gun/lib/axe.js:57:42)
    at listOnTimeout (node:internal/timers:559:17)
    at processTimers (node:internal/timers:502:7)
```

That happens in some node environments from time to time.
2022-03-17 19:00:10 -07:00
Mark Nadal
f6b65c8e7e Merge branch 'master' of http://github.com/amark/gun 2022-03-16 17:04:21 -07:00
Mark Nadal
5ea98f48e3 fix open, warn if too fast 2022-03-16 17:02:38 -07:00
Mark Nadal
41c2f64ef2
github debug stats 2022-03-13 13:39:13 -07:00
Mark Nadal
2ff35aa316 Merge branch 'master' of http://github.com/amark/gun 2022-03-11 05:17:06 -08:00
Mark Nadal
c4a613a971 json is lame 2022-03-11 05:17:01 -08:00
Mark Nadal
2bbbe36e13 prep for stash 2022-03-06 04:53:15 -08:00
Malted
532b59b098
Remove erroneous apostrophe (#1213) 2022-02-27 04:16:37 -08:00
Orimay
d53d157f19
New GUN typings (#1205)
* New GUN typings

* Fixing comments

* Gun schema is now supported

Co-authored-by: dbaranov <dbaranov@bellintegrator.com>
2022-02-18 23:17:41 -08:00
Anton W
1051e477ba
Fix offer payload (#1210) 2022-02-18 23:03:44 -08:00
bbayazit16
2c3e7d55fd
Remove val from comment (#1207)
Line 78 uses ".once", but "val" is used in comment at line 79.
2022-02-18 22:59:38 -08:00
Orimay
ba7c3e8bda
NaN -> false (#1200) 2022-02-04 03:00:10 -08:00
Orimay
ddaf523a98
NaN -> false (#1202) 2022-02-04 02:59:53 -08:00
Orimay
666569f1d7
NaN -> false (#1201) 2022-02-04 02:58:40 -08:00
Orimay
05f497778a
@Orimay put validation: adding '-Infinity' as an invalid case; speeding validation up (#1189)
* Adding '-Infinity' as an invalid case

Adding '-Infinity' as an invalid case, not handled before
Skipping extra checks until needed (like NaN, Infinity)
Checking for 'null' first as for more common case than invalid 'undefined'

* Adding '-Infinity' as an invalid case

Adding '-Infinity' as an invalid case, not handled before
Skipping extra checks until needed (like NaN, Infinity)
Checking for 'null' first as for more common case than invalid 'undefined'

* Adding '-Infinity' as an invalid case

Adding '-Infinity' as an invalid case, not handled before
Skipping extra checks until needed (like NaN, Infinity)
Checking for 'null' first as for more common case than invalid 'undefined'

* Fixing tests to handle -Infinity

* Removing useless shim import

* Removing useless shim import

* Removing redundant undefined check

* Removing redundant undefined check

* Removing redundant undefined check

* ES5 compatibility

Co-authored-by: dbaranov <dbaranov@bellintegrator.com>
Co-authored-by: Mark Nadal <mark@gun.eco>
2022-02-04 02:41:22 -08:00
Promaethius
f85f55c5a1
test escaped backslash (#1156) 2022-02-02 22:45:55 -08:00
david smooke
ce20e0787d
Update README.md (#1181) 2022-01-15 21:22:57 -08:00
Mark Nadal
852b77f49a Merge branch 'master' of http://github.com/amark/gun 2021-12-16 01:21:04 -08:00
Mark Nadal
6780cb4334 better error + fix nested .once hanging on non-existent node when link does exist 2021-12-16 00:05:43 -08:00
Emad Kheir
77162fcb68
YSON Fixes (#1170)
* Rewrite YSON, fixes #1155

* Modify test file so it now includes a backslash to test YSON's escaping functionality

* Moved new yson implementation to ison.js

* Update YSON imports to ison.js on radisk, server and stats

* Update test to use ison.js and added emojis to test strings with unicode characters
2021-12-07 22:47:48 -08:00
tedd pasta
d1f13c86c6
make change to lib/unset.js (#1167)
* change stats.radata

* remove stats.radata file creation since fs.writeFil will do it any way

* require path and use last directory of opt file path or use radata

* make unset change
2021-12-02 18:39:37 -08:00
Natnael Teferi
9413142c80
ReactNative/Expo fix for SEA (#1169) 2021-11-28 20:05:15 -08:00
Mark Nadal
6d3ea2ecc5
Thanks to @DimensionDev https://mask.io/ for supporting! 2021-11-24 17:36:53 -08:00
Jairus Joer
3c5de47c42
Change Dockerfile python package to python3 (#1166)
As of version 3.12, the python package will no longer be supported in favor of explicitly chosen packages python2 or python3.
2021-11-23 13:52:01 -08:00
tedd pasta
396b367e4e
change stats.radata file (#1161)
* change stats.radata

* remove stats.radata file creation since fs.writeFil will do it any way

* require path and use last directory of opt file path or use radata
2021-11-23 13:51:25 -08:00
Mark Nadal
1969d48e0f
Thanks @mikestaub for donating!!! :) <3 2021-11-21 17:49:23 -08:00
Mark Nadal
0441aa332d
Update axe.js 2021-11-15 02:03:41 -08:00
Mark Nadal
009be7e534
Thanks https://ghostdrive.com (Roman Tykhovsky) for donating!!! :) 2021-11-13 18:32:14 -08:00
Mark Nadal
74d2b077cd
Thank you Fabian Stamm @hibas123 for donating!!! 2021-11-10 17:10:33 -08:00
Emad Kheir
50af2d52ad
Halt rap function on undefined key (#1157) 2021-11-05 23:52:08 -07:00
Hector
3b8eb16960
✂️ Path shortened ! (#1141)
* Fix the "/" bug.

* Update the "hub-test" to make them work everywhere!

* 🤛Fix the "/" bug !

* Fix indentation

* ✂️ My new quest shall be to shorten the file paths !

* 👽 Remove Chokidar from dependencies

* 👽 Remove chokidar from dependencies!
2021-10-29 23:20:16 -07:00
MaciejDot
395c2b8f98
partly repair gun types (#1154)
* partly repair gun types

* added types tests
2021-10-29 23:15:02 -07:00
Jeff Hykin
17af355ac1
4 lines, improve logging slightly (#1153)
* use gun.log.once for logging consistency

* -
2021-10-29 23:09:37 -07:00
Mark Nadal
49973ac522
Update README.md 2021-10-15 02:03:21 -07:00
Mark Nadal
90c7fb813e
it did not, so undo 2021-10-13 22:34:24 -07:00
Mark Nadal
34f53a20fb
let's see if this works? 2021-10-13 22:23:50 -07:00
Hector
0b158667a7
Update hub ! (#1134)
* Fix the "/" bug.

* Update the "hub-test" to make them work everywhere!
2021-09-16 08:54:01 -07:00
Mark Nadal
edf6c5f38d fix @yokowasis bug found + house keeping. 2021-09-13 23:02:45 -07:00
Gál Péter
c97ac0002c
Fix: type error shown when using generated keypair (#1131)
* fix errors shown when trying to use correct keypairs

* mark only (really) used parameters as required

- a callback is optional, because we could just use the async/await methodology to recieve the keypair
- when signing only `priv` is used
- when verifying only `pub` is used
- both when encrypting/decrypting only `epriv` is used
2021-09-11 10:02:08 -07:00
Mark Nadal
d7282be14b
yaml y u no multi line comment? 2021-09-06 04:43:31 -07:00
Mark Nadal
f8f2fc502d
@tcurdt docker no login so it fails, disabling till fix 2021-09-06 04:21:36 -07:00
mimiza
63b0043076
Improve SEA: .auth, .certify, check.pub (#1130)
* Improve SEA.certify

- Improve certify() logic
- "Blacklist" renamed to "block". BLACK LIVES MATTER!
- Fix user.is.alias, now it doesn't contain full pair any more.

* SEA.certify wire logic tests

The "block" (former "blacklist") feature is not working yet (due to a bug in gun)

* skip certify block feature unit test
2021-09-05 22:40:36 -07:00
Luca Guzzon
89c5286a52
A silly typo ... (#1127)
I think that "process.AWS_REGION" should be "process.env.AWS_REGION", right?
2021-09-05 22:38:45 -07:00
Torsten Curdt
31971c0c51
publish master and releases (#1126)
* no artifacts, not required

* renamed

* cleanup and comments

* pass build args

* two-step docker build

* renamed to as we might push to any docker repo

* rename and cleanup

* added some docs and expectation about the CI/CD and release information

* combined all workflows into steps
2021-09-05 22:34:13 -07:00
Mark Nadal
7dedd0797c
yummier quickstart! 2021-09-02 04:48:45 -07:00
Mark Nadal
e52e5821c1
add intro video! 2021-09-02 04:11:10 -07:00
Mark Nadal
eafab334a9
clean, update, housekeep :) 2021-09-02 03:56:50 -07:00
Mark Nadal
d1a59e5d6f
Update install.sh 2021-09-02 03:22:08 -07:00
Mark Nadal
d178d4a095 what about this new peer? 2021-08-31 10:59:47 -07:00
Mark Nadal
8cd8c4f6fb Merge branch 'master' of http://github.com/amark/gun 2021-08-31 09:38:26 -07:00
Mark Nadal
e63504a8df what about this volunteer peer? 2021-08-31 09:38:22 -07:00
Mark Nadal
ae0ffadc29
Update README.md 2021-08-30 23:54:15 -07:00
Mark Nadal
bdf4717d2d
Thanks @timjrobinson ! :) 2021-08-30 21:05:29 -07:00
Hector
084e2907da
🎉 Introducing a new feature ! (#1121)
* 🎉 .hubignore is ready !

* 🧪 Add tests for hub.js !
2021-08-30 14:29:55 -07:00
Torsten Curdt
0eec835c31
cleaning up and improving the CI/CD pipeline (#1122)
* no artifacts, not required

* renamed

* cleanup and comments

* pass build args

* two-step docker build
2021-08-30 14:26:44 -07:00
Mark Nadal
8e2f12542d ENV vars to disable RAD 2021-08-29 10:17:38 -07:00
Mark Nadal
5df08f91cb Merge branch 'master' of http://github.com/amark/gun 2021-08-25 21:27:29 -07:00
Mark Nadal
b8450ceab9 Cleaner installs, more compatible environments 2021-08-25 21:27:25 -07:00
Hector
1be6c5ed62
📦 You can now disable messages from hub & more... (#1120) 2021-08-24 22:25:09 -07:00
Hector
4db88079b5
👺 Add a .replace() to delete all the “\n’ (#1118) 2021-08-24 02:18:06 -07:00
Mark Nadal
6cb7261ac2 unbuild fixes & shim temporary deprecations for publication 2021-08-23 00:59:58 -07:00
Mark Nadal
dac9e8d059 fix synchronous runs, don't skip wait, fire first, aggregate but error if need fire, 2021-08-23 00:49:22 -07:00
Mark Nadal
6336cc66ae starting documentation etc. refinements piece by piece 2021-08-21 23:26:37 -07:00
Mark Nadal
8e4128b474 unbuild gun - after 1 year of merges! 2021-08-21 21:27:55 -07:00
Mark Nadal
cd45008433 unbuild sea 2021-08-21 21:27:00 -07:00
Mark Nadal
087704ec6b
Begin 1 Years worth of Merges... (#1116)
* tmp for hn issue

* log top to stats

* test for guntest peer

* try big messages

* parse time?

* what bin/node is 11ms?

* be normal for hnoon

* tolerate 0.5s

* try 3s for hnoon?

* stop empty gets

* tmp for guntest

* back to normal

* check hash time

* back to normal in hear

* screen / upload / play / pause

* merge latest npm release into manhattan

* merge master 0.2020.421 into manhattan manually

* WIP

* manually merge from master

* gatling

* Update upload.html

* work in progress...

* yson panic chat basic

* after `.put(` walk

* restructure acks

* messy but 2 units passing!

* put recursive once on map

* basics

* have NTS use DAM + fix other utils

* Rewrote nts

* Allow passing test cli args.

Before, no CLI args would be passed when running `npm test`. Keeping the `mocha` at the end of the test script allows passing CLI args to Mocha.

* put back scan & once tweak

* PANIC user paste OK

* manhattan sea

* stub out nts for now

* AXE tweak

* tweak for quick first prod testing

* tweak for first in-prod testing

* tweak

* tweak

* sketchy in-prod debug attempt

* caught it? maybe? now restore

* Create download-log.html

* stub out yson test prod?

* ugh, gotta see what is going on

* move dl

* gonna stop doing commit messages for in-prod test/tweaks/debugging

* a

* p

* squelch

* console stats

* stats

* stop travis

* restore yson

* ahhh no file access without sudo

* mem

* no stub

* fix axe

* bump

* back to in-prod testing, isolate/stub out code

* stub all out for 17K ? CPU ? test

* stub dup gc

* ugh main stub

* does this stop url format blocking?

* re-add dup

* no top :(

* will this work?

* get ack stats?

* a map chain may ask for data not a root soul chain

* move proper logic into .get(

* how 2019 compat?

* a couple more!

* more tests passing! :D :)

* even more! SO EXCITING :D

* Am I alive?

* wow I can't believe it works like this

* THANK YOU @rogowski !!!!!!

* Create trace.html

Adding tracing to debuging.

* @rogowski is a super star :) :) :)

* Update trace.js

Change `Gun.logs` to `Gun.traces` and `Gun._log` to `Gun._trace`.

* Update trace.html

Change `Gun.logs` to `Gun.traces` and `Gun._log` to `Gun._trace`.
Overload get,put,on,map

* @rogowski approved of these trace changes :)

* Update trace.html

More decoupled.

* Update trace.js

More decoupled

* 2 steps backwards, 1 step forward?

* back where we ( @rogowski ) started :P

* YAYAYAYAYAYAYAY past where we started at!

* safer to have it here

* slight tweak? Let's see how long it lasts.

* merge checks we left out during consolidation

* ugly common.js for @rogowski

* slightly better

* amazing map discovery + don't clear on not found if data exists

* onto next test...

* all caught up!!! Now update tests from graphify to statedisk

* Update common.js

Tests updated from graphify to statedisk.

* easy to debug & fix thanks to @rogowski 's test upgrades & trace!

* hmm, IDK if we should support this anymore?

* support once chaining?

* check if listener order is earlier than write

* in-process message passing needs to clean itself of flags for now

* ack to chains that can't be reached

* call sub chains on clear/empty WIP

* call sub chains clear/empty OK!

* into unlink. Clean/refactor later.

* oh that was nice

* self check not needed?

* test was poorly constructed?

* refactor unlink to cleaner logic

* Will you blame me for this? Special case, maybe later move to cleaner place?

* use stun's run id instead.

* cleaner unlink logic

* better map, link, and unlink logic.

* unstub relay

* refactor unlink

* invert

* if prev value would have caused unlink, do not unlink again.

* w000h00! Best unlink so far.

* woops, fix unlinking nested

* unsubscribe nested maps (working, tho possible perf regression? check)

* put check soul

* add default uuid

* improved browser peer retry logic, let devices sleep, etc.

* Chaining API unit tests passing!

* merge new panic tests into here to test

* add panic utils

* fix long streaming timeout/expiry issue, update examples

* yield generating test data

* yeah, adapter errors (like out of storage) should not affect sync/networking logic, that was a bad experiment

* git glitch?

* some mid debugging fixes but maybe scary changes, hopefully safe to revert here except dub

* SEA unit tests passing!!! Needed quite a few fixes on async write queue stuff.

* optionally make auth async

* revise/correct set

* Fix reverse boundary check

* Add extra tests, catch bad guy, obliterate bug.

* chat app with emoji examples

* handle empty string keyed objects

* starting lex support

* tweak for lex

* woops! lexical alphabetical oopsies. That was bad.

* upload either way

* debug

* start

* fix

* fix

* clean + feature

* update dependencies in package.json (#1086)

* rad lex once map once

* axe polyfill for now

* oops log

* oops maybe without this it crashed the peer

* what on earth happened to my browser/OS? "unplug & plug it back in" restart seemed to fix it.

* oh, don't memory leak req/res asks. :/ duh!

* no accidental #soul.""

* ugh, still have to sort :(, really should polyfill weakmap then

* oops, pluck needs new object to go into

* oops, make sure soul is passed

* updating deprecated functions

* begin AXE. Next: load balance!

* Update sea.js

* keys are dangerous!

* AXE round robin load balance

* better ash hash checking

* lS reuse in-mem reply chunking

* state machine!!!

* RAD needs to pass cache misses.

* updating deprecated functions (#1088)

* update dependencies in package.json

* updating deprecated functions

* remove where.gundb.io

* Bring SEA.certify into manhattan branch (#1092)

Co-authored-by: Radu Cioienaru <radu@projectmanager.com>

* fix rad, make get() hookable

* rad browser tests seem to be passing!

* reverse user random side, add err, update styles, + more

* fix pack/max, update dom

* paste!

* of course it'll dedup cause it just called track on hear, fix

* 📦 Adding the hub feature to this branch & improvements. (#1102)

* 📦 Adding the hub feature to this branch.

* 🗑 Removed the container for speed improvement !

* 📝 I added some comments to the code.

Co-authored-by: Hector <fairfairytotor@gmail.com>
Co-authored-by: Hector <pro.hector.kub@gmail.com>

* Update axe.js

* 🦅 Wrap everything in a try & catch for error handling…  (#1105)

* 🦅 Wrap everything in a try & catch for error handling & speed improvement.

* 📦 Finally here : opt.file for the hub feature !

* 📦 Finally here : opt.file for the hub feature !

And also : fixed indentation 😋

Co-authored-by: noctisatrae <pro.hector.kub@gmail.com>

* probs better this way, safer

* moved test/axe tests to test/panic/axe.

* New test: axe load balance.

* axe test: webrtc data balance(fix paths and file renamed).

* test axe: renaming webrtc file.

* axe test: separating webrtc test for data_balance.

* axe test: test only with the relay(without webrtc).

* Update sea.js

Same as https://github.com/amark/gun/pull/1062

* Update gun.js

var tmp

* Update upload.js

* merge, update stun

* SEA.certify wire logic + unit tests (#1110)

* SEA.certify wire logic + unit tests

* picking white hair

* ack err

* axe tests using puppeteer.

* change stun system

* ~20lines

* put use parent soul link if need

* handle errors

* finally seems fixed

* cb not to

* relay

* nasty bug! Don't crash, tho need to find what causes it

* undo local changes/notes to self

* deprecation warnings

* "old" data to test against

* oops, forgot I played with ascii

* debug

* in-prod check: sites

* in-prod isolate

* gotta find this, by stubbing out

* where?

* will this work?

* clearly not, lol what's the point then? maybe like this

* and again

* must we?

* USE THIS MANHATTAN VERSION

* clean

* better panic hints

Co-authored-by: Robin Bron <finwo@pm.me>
Co-authored-by: Pavel Diatchenko <diatche@users.noreply.github.com>
Co-authored-by: rogowski <163828+rogowski@users.noreply.github.com>
Co-authored-by: I001962 <i001962@gmail.com>
Co-authored-by: Adriano Rogowski <rogowski.adriano@gmail.com>
Co-authored-by: Radu <cetatuie@gmail.com>
Co-authored-by: Radu Cioienaru <radu@projectmanager.com>
Co-authored-by: Hector <46224745+noctisatrae@users.noreply.github.com>
Co-authored-by: Hector <fairfairytotor@gmail.com>
Co-authored-by: Hector <pro.hector.kub@gmail.com>
Co-authored-by: Martti Malmi <sirius@iki.fi>
Co-authored-by: mimiza <dev@mimiza.com>
2021-08-21 21:19:29 -07:00
mads
e7afd231eb
update some things to make docker work again (#1095) 2021-08-08 10:32:54 -07:00
chance
da573640b3
Fixes a few typos and small grammatical errors in the type definitions located at types/chain.d.ts (#1106)
* Adds a note about deploying web apps where an environment variable should be changed to avoid GUN warnings being treated as errors during CI

* Fixes a few typos and small grammatical errors in the type definitions types/chain.d.ts
2021-08-05 20:28:35 -07:00
chance
44fd7bee89
Adds a note about deploying web apps where an environment variable should be changed to avoid GUN warnings being treated as errors during CI (#1101) 2021-08-03 21:40:46 -07:00
Hector
5ba2d9b44f
📦 Adding the new hub feature ! (#1099)
Co-authored-by: Hector <fairfairytotor@gmail.com>
2021-08-03 13:05:51 -07:00
Bernie Telles
5d5432a9b5
Fix set/get parameters to use Record<string,*>. (#1094)
* Add TypeScript test file & tsd as devDep to run it

This helps those who want to contribute to the TypeScript definitions
by adding:
 * a tool called `tsd,`
 * its corresponding configuration in package.json
 * a teste file index.test-d.ts

Also add a extra line between type definitions to improve readability.

* Fix set/get parameters to use Record<string,*>.

The previous implementation did not allow users to
retrieve items that were inserted using "set," and
it incorrectly used ArrayOf to extract record types.

The previous implementation also prevented users
from ever inserting an array. Although it the database
does not handle arrays elegantly, it allows the user
to insert them. Typing should not prohibit use of
arrays.
2021-07-30 00:20:32 -07:00
Bernie Telles
70eb769209
Add TypeScript test file & tsd as devDep to run it (#1091)
This helps those who want to contribute to the TypeScript definitions
by adding:
 * a tool called `tsd,`
 * its corresponding configuration in package.json
 * a teste file index.test-d.ts

Also add a extra line between type definitions to improve readability.
2021-07-27 13:02:47 -07:00
Mark Nadal
8c8b513716
Update rs3.js 2021-07-15 23:03:56 -07:00
Mark Nadal
5bafcea9f3
thanks @ajartille 's http://ajar.org/ foundation! 2021-07-14 06:16:50 -07:00
Mark Nadal
a362460b0f
Thank you @nmauersberg !!! 2021-07-10 00:06:24 -07:00
Mark Nadal
617c7ac2f1
Thanks @sjuxax ! 2021-07-08 15:52:31 -07:00
Egi Hasdi
fdedf8e59d
[type definition] use keypair for user.auth (#1085) 2021-06-30 17:27:49 -07:00
Johnny Dallas
b1b0432983
Add zeet.co deploy button (#1075) 2021-06-22 15:23:54 -07:00
marcomow
fe9ea62f61
Fix wrong element's dynamic identifier, semicols (#1080) 2021-06-16 22:23:37 -07:00
nsreed
10b42525ad
update on-recovery PANIC test to run default & radisk configurations (#1078) 2021-06-16 11:56:34 -07:00
nsreed
fc10c250c9
add load-browser-scripts utility (#1077) 2021-06-02 16:23:47 -07:00
Martti Malmi
0f9ebfc454 update iris-lib in package-lock.json 2021-05-31 16:07:22 +03:00
Martti Malmi
7925598cd8 update iris 2021-05-31 13:52:35 +03:00
Mark Nadal
8b9f8915f2
Update README.md 2021-05-10 12:36:07 -07:00
Mark Nadal
804bf04e50 version bump 2021-05-06 00:37:48 -07:00
nsreed
2168e3e2d7
add options argument to set/put method signatures (#1067)
* chain.d.ts: add options argument to put & set

* chain.d.ts: set cb may be null when providing opts

Co-authored-by: Norman Reed <norman.s.reed@gmail.com>
2021-04-11 22:58:38 -07:00
Mike
5ca97d88a3
Add git to Dockerfile apks (#1061)
Git needs to be added to the docker image or the build fails at the npm install stage.

_Looks at iris_
...
npm ERR! path git
npm ERR! errno -2
npm ERR! enoent undefined ls-remote -h -t ssh://git@github.com/irislib/iris-messenger.git
...
2021-04-11 22:57:45 -07:00
RICHΛRD ΛNΛYΛ
429b011955
Fixing non-existent err variable in localStorage (#1044)
* Fixing non-existant err variable in localStorage

* Update localStorage.js
2021-04-11 22:55:44 -07:00
RICHΛRD ΛNΛYΛ
e9ed2e5a02
Improving websocket retry logic to support no retries (#1045)
* improving retry logic

* Trigger Build
2021-04-11 22:51:40 -07:00
Mark Nadal
331ea85c86
Rename emoji to emoji.html 2021-04-11 21:52:20 -07:00
Mark Nadal
183555d793
Create emoji 2021-04-11 21:51:59 -07:00
Mark Nadal
655bec7b16
Rest In Peace Sergeant Major de Vries 🔫 🔫 🔫 🔫 🔫 🔫 🔫 🔫 🔫 🔫 🔫 🔫 🔫 🔫 🔫 🔫 🔫 🔫 🔫 🔫 🔫 2021-04-06 04:34:06 -07:00
damian-tripbuddi
4a84984699
Correctly referring to process.env.AWS_REGION and initializing Store with s3 if opts.s3 has been configured. (#1064) 2021-04-02 14:49:01 -07:00
I001962
4dc672eeb4
Fix safari sea issues (#1062) 2021-03-19 13:24:40 -07:00
Mark Nadal
508d38cd1b
Update rad.js 2021-03-04 14:37:31 -08:00
Carlos Vega
45dd008cb7
(WIP) Bug rad reverse sorting (#1021)
* bug: fixes issue with reverse sorting

* chore: updates unit test

* bug: new array not needed as filter already return a new one
2021-03-04 14:35:52 -08:00
jojobyte
1c25266935
Keep devDependencies installed in docker image (#1047) 2021-03-04 14:32:14 -08:00
Yang Hanlin
ae6d5cb6bf
Improvements to automatic redirect on loading SEA module (#1055)
* Warn on automatic redirect to HTTPS

* Don't redirect to HTTPS on loopback IP addresses

* Check if current location is a local loopback IP address (127.0.0.0/8) and do not redirect
2021-03-04 14:30:41 -08:00
davay
5be57c3fb1
Fix: certificants' pub strings now pass to cert (#1058)
`certificants.indexOf('*') !== -1` appears to be correct way of using this function. `certificants.includes` may be another option, but it may be too modern for compatibility concern
2021-03-04 14:21:47 -08:00
Martti Malmi
4ded14186f
Update app.json 2021-01-23 00:11:46 +02:00
Martti Malmi
88cfae52b4
Add PEERS env variable to app.json for Heroku 2021-01-23 00:07:03 +02:00
Martti Malmi
8de6ce5ca2
Don't prune devDependencies on Heroku (#1046)
* NODE_ENV=development for heroku deployment

* Use NPM_CONFIG_PRODUCTION=false instead

* Update app.json

* Update app.json
2021-01-19 11:51:34 -08:00
Mark Nadal
60d94180e2
Merge pull request #1043 from mmalmi/startpage
New landing page
2021-01-18 01:21:50 -08:00
Martti Malmi
43eae41c35 move images to iris, move iris to devDependencies 2021-01-16 16:21:25 +02:00
Martti Malmi
d141c6e403 togglable sidebar on mobile 2021-01-14 16:50:12 +02:00
Martti Malmi
edccff60a7 redo landing page using iris components 2021-01-14 12:48:11 +02:00
Mark Nadal
cb3f8600d9
Merge pull request #1029 from jussiry/master
Wave.js comments and spaces to structure code
2020-12-23 02:18:55 -08:00
Mark Nadal
00f75aff9a
Merge pull request #1036 from mimiza/master
SEA.certify(): Urgent bug fix for blacklist
2020-12-18 12:29:33 -08:00
MIMIZA
1bbc308ceb fix bug 2020-12-19 01:14:26 +07:00
MIMIZA
6e0a6fe225 SEA.certify: fix bug 2020-12-19 00:24:03 +07:00
Mark Nadal
a6b6b7f2ee
Merge pull request #1035 from mimiza/master
SEA.certify() small bug fix + more unit test
2020-12-16 01:20:45 -08:00
MIMIZA
49b2937421 SEA.certify() small bug fix + more unit test
There is a small bug and this is a fix for it. More unit tests added.
2020-12-16 15:51:37 +07:00
Mark Nadal
be0b1cefb2
Merge pull request #1034 from mimiza/master
change SEA.certify() && check.pub()
2020-12-14 23:09:07 -08:00
MIMIZA
0a91b8079b change SEA.certify() && check.pub() 2020-12-14 15:27:46 +07:00
Mark Nadal
c17a14b53a
Merge pull request #1033 from mimiza/master
SEA.certify: replace RegEx with RAD/LEX, force path to contain Certificant Pub
2020-12-13 12:33:05 -08:00
MIMIZA
e2a92bd61e small fix 2020-12-14 02:36:50 +07:00
MIMIZA
34d7f6fc70 small fix 2020-12-14 02:28:46 +07:00
MIMIZA
d8d037fc81 SEA.certify: replace RegEx with RAD/LEX. New feature: Path must contain Certificant Pub
- RegEx replaced with RAD/LEX
- NEW FEATURE: If LEX has key "?" and lex['?'] contains '*': check if Certificant Pub is in Path
- Unit tests updated
Official doc will be written after this PR is merged.
2020-12-14 02:23:26 +07:00
Mark Nadal
7d12a82458
Merge pull request #1028 from mimiza/master
Add Blacklist feature to SEA.certify
2020-11-29 15:01:31 -08:00
Jussi Rytkönen
227685cedf Merge branch 'master' of github.com:amark/gun 2020-11-23 13:10:16 +02:00
Jussi Rytkönen
1e1cad8bbd wave.js comments and structured spaces 2020-11-23 13:09:21 +02:00
MIMIZA
4d0bcab3b8 Add Blacklist feature to SEA.certify
Now SEA.certify supports Blacklist. For more details, please read doc here: https://gun.eco/docs/SEA#certify
2020-11-23 12:21:48 +07:00
Mark Nadal
b3e5b21f86
Merge pull request #1020 from mimiza/master
Add SEA.certify(), upgrade SEA check.pub(). Tests passed. Bugs free.
2020-11-18 22:59:40 -08:00
Mark Nadal
deeb87e31b
Merge pull request #1026 from tcurdt/master
CI/CD setup
2020-11-18 22:57:32 -08:00
Torsten Curdt
0770648219 switch version back to master 2020-11-15 01:10:20 +01:00
MIMIZA
5ee816191a Improve SEA check.pub() 2020-11-14 13:44:20 +07:00
Torsten Curdt
ab5e38b367 not sure how it happend - but fixed the tab mixup 2020-11-13 20:56:00 +01:00
Torsten Curdt
002680ede4 removed debug 2020-11-13 20:09:00 +01:00
Torsten Curdt
e1de0c875e unbuild needs uglifyjs 2020-11-13 20:09:00 +01:00
Torsten Curdt
d7dd65babf debug publish 2020-11-13 20:09:00 +01:00
Torsten Curdt
fece267263 reduce the test matrix because of flaky test 2020-11-13 20:09:00 +01:00
Torsten Curdt
bff7804b5b disable windows builds for now 2020-11-13 20:09:00 +01:00
Torsten Curdt
1eed73af48 proper directory 2020-11-13 20:09:00 +01:00
Torsten Curdt
afa7abc646 Create main.yml
wtf, github
2020-11-13 20:09:00 +01:00
Torsten Curdt
8a7d1ce2fc added ci, artifact cleanup, and pushing releases to dockerhub and npm 2020-11-13 20:09:00 +01:00
Torsten Curdt
52590d96b9 personal package space for testing 2020-11-13 20:09:00 +01:00
Torsten Curdt
4f3c5f0f3d re-add peer dep (was a npm problem) 2020-11-13 20:09:00 +01:00
Mark Nadal
57d1919c68
Update meta.js 2020-11-13 05:28:40 -08:00
Mark Nadal
1703e6003c
Merge pull request #1025 from jussiry/master
meta lib update: open/close on hover and back button fixes
2020-11-13 05:11:55 -08:00
Jussi Rytkönen
4a66bface6 meta lib: removes extra indentation 2020-11-13 15:02:14 +02:00
Jussi Rytkönen
badb490d50 Merge branch 'master' of github.com:amark/gun 2020-11-13 14:31:26 +02:00
Jussi Rytkönen
807ec9ef25 meta lib update: open/close on hover and back button fixes 2020-11-13 14:30:01 +02:00
Mark Nadal
bafb25dcfa
Merge pull request #1023 from jussiry/master
Wave update
2020-11-11 12:48:07 -08:00
Jussi Rytkönen
cf5c00d82c Merge branch 'master' of github.com:amark/gun 2020-11-10 13:14:11 +02:00
Jussi Rytkönen
77c39a2035 wave example and library update 2020-11-10 13:13:39 +02:00
Mark Nadal
564c6f3100
Merge pull request #1022 from mmalmi/peers-env-variable
Peers env variable
2020-11-09 13:18:34 -08:00
Martti Malmi
c254f4c699
remove console.log 2020-11-09 16:09:34 +02:00
Mark Nadal
0875201558
Merge pull request #1013 from rogowski/test-panic-lexical
Test RAD lexical queries.
2020-11-08 23:04:18 -08:00
Mark Nadal
803e1d4938
Merge pull request #1014 from finwo/adapter/leveldb-radless
RAD-less leveldb adapter
2020-11-08 23:00:57 -08:00
MIMIZA
5701b8b581 SEA.certify unit tests 2020-11-07 16:27:08 +07:00
Martti Malmi
2777d87c4d remove react-native-webiew-crypto dependency which breaks the build 2020-11-04 11:07:23 +02:00
Martti Malmi
ca003747b7 use PEERS env variable in http.js 2020-11-03 17:05:40 +02:00
mimiza
c95b99d376
Added a warning console.log to SEA.certify() 2020-10-28 19:38:14 +07:00
mimiza
c686a15980
Small fix 2020-10-27 16:55:22 +07:00
mimiza
04bbfdd2eb
Small fix 2020-10-27 15:44:45 +07:00
MIMIZA
5d755af301 Merge branch 'master' of https://github.com/amark/gun 2020-10-27 14:35:36 +07:00
MIMIZA
bac237fa89 SEA.certify() completed. SEA check.pub() upgrade completed. Tests passed. Bugs free. 2020-10-27 14:26:11 +07:00
MIMIZA
37da3cc8ea improve SEA Cert security, still need much improvement in check.pub, unfinished 2020-10-26 23:53:04 +07:00
MIMIZA
be6dcf0f99 improve SEA Cert security, still need much improvement in check.pub, unfinished 2020-10-26 23:48:20 +07:00
MIMIZA
ebe8345090 finished prototyping, but need much refactoring 2020-10-26 15:56:04 +07:00
Mark Nadal
4dc88e9ea8
Merge pull request #1019 from bioshazard/bug/GUN-935
Fixed issue 935. Detects zero length key
2020-10-25 15:09:52 -07:00
MIMIZA
80c74d254b refactoring cert prototype & check.pub, unfinished. 2020-10-24 23:27:53 +07:00
MIMIZA
97bbdd9b74 cert injection && validation done 2020-10-21 01:36:57 +07:00
mimiza
622a252722
prototyping SEA.certify() 2020-10-19 22:53:57 +07:00
Joe Still
37726f6e68 Fixed issue 935. Detects zero length key 2020-10-18 17:03:48 -05:00
Robin Bron
84097d7e68 Added radless leveldb adapter 2020-09-27 01:55:12 +02:00
Adriano Rogowski
69165f69f5 Test RAD lexical queries. 2020-09-24 21:46:29 -03:00
Mark Nadal
05349a5c68
Merge pull request #1010 from mmalmi/infinite-scroll-test
infinite scrolling example
2020-09-21 13:26:03 -07:00
Martti Malmi
fe57813721 simple panic test that scrolls through 1500 entries 2020-09-18 15:05:23 +03:00
Martti Malmi
773d5cba25 cleanup 2020-09-18 11:25:08 +03:00
Mark Nadal
7e9ec5d93a
Merge pull request #1007 from diatche/feature/panic2
PANIC test semi-automation using Puppeteer
2020-09-17 22:30:56 -07:00
Martti Malmi
866e8594d6 scrolling up from bottom 2020-09-17 23:28:33 +03:00
Martti Malmi
11bdf2211e empty img src in between 2020-09-17 22:03:58 +03:00
Martti Malmi
811a1b8963 infinite scrolling example, panic test wip 2020-09-17 17:16:14 +03:00
Pavel Diatchenko
a607bdd004 Cleanup puppeteer on exit. 2020-09-17 12:37:47 +12:00
Pavel Diatchenko
33b29f4ee6 Added puppeteer cleanup method. 2020-09-17 12:33:04 +12:00
Pavel Diatchenko
fee7473ccb Use puppeteer if available. 2020-09-14 16:12:50 +12:00
Pavel Diatchenko
fd6752cbd6 Added browser open utility method. 2020-09-14 16:06:49 +12:00
Mark Nadal
a89ca678bb
Merge pull request #1001 from shocknet/shocknet-tests
Shocknet tests
2020-09-11 14:05:18 -07:00
Mark Nadal
c82e0da589
Update README.md 2020-09-04 17:35:18 -07:00
Mark Nadal
b78bbc322b
Update .dockerignore 2020-08-26 13:46:02 -07:00
Mark Nadal
35c8a75a00
Merge pull request #1000 from estebanrfp/master
added vanilla js version of video.html
2020-08-19 03:50:59 -07:00
estebanrfp
bf68704063 added vanilla js version of video.html 2020-08-16 22:59:09 +02:00
Mark Nadal
5cbd5e3943
Merge pull request #990 from amark/node-client-auth
add: test node as client SEA auth
2020-07-24 01:35:18 -07:00
Mark Nadal
bd417b9f04
Merge pull request #991 from Joncom/on-recovery
Test if gun.on() callback fires after relay peer crashes
2020-07-24 01:08:52 -07:00
sirpy
f723c69d7c
add: test node as client SEA auth 2020-07-19 00:07:06 +03:00
Daniel Lugo
a394a23aa1 saner name 2020-07-10 20:09:56 -04:00
boufni95
8161f14553 once -> then 2020-07-08 17:39:43 +02:00
boufni95
87b2dbe03d 6 -> 4 servers 2020-07-08 16:46:12 +02:00
boufni95
85191e8042 alice -> peer1 -> peer2 -> bob 2020-07-08 16:34:10 +02:00
boufni95
18aebaa4a1 handshake tests 2020-07-08 15:55:24 +02:00
boufni95
34f1be198e shocknet tests 2020-07-06 14:55:28 +02:00
Mark Nadal
97aa976c97
Merge pull request #970 from mimiza/fix-sea-sessionStorage
Fix user.create() callback not firing and user.create(pair)
2020-07-05 20:14:54 -07:00
Jonathan Commins
2ddb526323 Test if gun.on() callback fires after relay peer crashes 2020-06-24 22:08:57 -07:00
MIMIZA
a7bb4a840f Update sea.js 2020-06-24 10:55:27 +07:00
MIMIZA
feb54f024b Update sea.js 2020-06-24 10:42:03 +07:00
MIMIZA
e74f4a00e2 Update sea.js 2020-06-24 10:35:21 +07:00
MIMIZA
ef837a1018 alias = alias || pair.pub 2020-06-24 10:26:46 +07:00
MIMIZA
6d8f776200 Fix user.create()
BEFORE:
- user.create(pair) --> ~@[object Object]
- (noop == cb) --> callback will never fire because functions can't be compared like this. For example:
```
const a = b = function(){};
a == b // FALSE
```

AFTER:
- user.create(pair) --> ~@pair.pub
- callback is fired
2020-06-22 21:18:24 +07:00
MIMIZA
01cd2050d2 improve sea.js
Removed the small try/catch in recall() because there is already a bigger try/catch.
2020-06-22 08:59:39 +07:00
MIMIZA
edc122f63c Improve user.recall()
Wrap JSON.parse() in a try{}catch{}
2020-06-19 12:38:52 +07:00
Mark Nadal
3e678b8568
Merge pull request #964 from mimiza/fix-sea-sessionStorage
Fix sea.js issues #962, #957, #956
2020-06-17 10:43:41 -07:00
Mark Nadal
7c45ddb558
Merge pull request #961 from diatche/log-once-fix
Fixed missing Gun.log.once function
2020-06-14 20:34:44 -07:00
MIMIZA
e88a120a4e remove console.log 2020-06-15 08:40:00 +07:00
MIMIZA
486184e767 Switch to ancient technique
ES6 might not be reliable, so I decided to switch back to ancient JS. Removed spread operator (...args), replaced with "arguments"
2020-06-15 08:32:22 +07:00
MIMIZA
823df19593 Fix sea.js issues #962, #957, #956
BEFORE:
- user.recall() uses alias/pass -> not reliable, only works with auth(alias,pass), doesn't work with auth(pair)
- user.auth(pair, cb) -> cb is not a function

AFTER:
- user.recall() uses pair -> more reliable, works with auth(pair)
- user.auth(pair, cb) -> works
2020-06-13 11:55:19 +07:00
Pavel Diatchenko
0d53db7877 Fixed error "Gun.log.once is not a function". 2020-06-10 14:45:18 +12:00
Mark Nadal
f759ab1394 rfs should be after rs3 but both defer so rfs be before 2020-06-07 17:44:48 -07:00
Mark Nadal
9bc25e3647 do not axe up peers on puts 2020-06-07 15:55:53 -07:00
Mark Nadal
461bb7990a Merge branch 'master' of http://github.com/amark/gun 2020-06-07 15:17:58 -07:00
Mark Nadal
bfb43bf7a9 make sure to call next middleware 2020-06-07 15:17:52 -07:00
Mark Nadal
5706f46b00
Update README.md 2020-06-05 23:59:01 -07:00
Mark Nadal
6d02684036
Merge pull request #929 from DimensionDev/feature/reduce-docker-image
chore: reduce docker image
2020-06-03 13:34:56 -07:00
Septs
16d286538f fix dockerfile 2020-06-03 11:35:05 +08:00
Septs
4a74c65019 Merge remote-tracking branch 'upstream/master' into feature/reduce-docker-image 2020-06-03 11:34:40 +08:00
Mark Nadal
b356877a23 clean uploadS! 2020-05-23 23:47:43 -07:00
Mark Nadal
113fd7727d uploadS 2020-05-23 23:35:18 -07:00
Mark Nadal
bcad8f7ef3 uploads 2020-05-23 23:30:26 -07:00
Mark Nadal
47a841fbcd cleaner & smaller video & upload 2020-05-23 22:35:47 -07:00
Mark Nadal
c45950a771 Create upload.html 2020-05-23 21:40:34 -07:00
Mark Nadal
d56e06fec7 video example in ~90 lines 2020-05-23 20:08:30 -07:00
Mark Nadal
fcf105aaab #948 2020-05-22 21:34:47 -07:00
Mark Nadal
a48fd8c0a3 help robots 2020-05-21 17:42:39 -07:00
Mark Nadal
e4333397e4 clean package 2020-05-21 17:27:43 -07:00
Mark Nadal
022741dd90
Update README.md 2020-05-21 16:27:29 -07:00
Mark Nadal
98e0554944
Update README.md 2020-05-21 15:45:32 -07:00
Mark Nadal
d333738b0a
Merge pull request #949 from GoodDollar/minor-rn-changes
Minor rn changes
2020-05-21 14:51:20 -07:00
Mark Nadal
00c48f172b deprecate unofficial opt 2020-05-21 14:48:07 -07:00
Mark Nadal
4eb7d0d383 stupid lock file ugh travis 2020-05-21 14:44:35 -07:00
Mark Nadal
9e811e882c tell Travis to use module :P 2020-05-21 14:40:19 -07:00
Mark Nadal
125e1384c1 fix RN by moving require 2020-05-21 14:20:30 -07:00
Hadar Rottenberg
55b9231d47 add: info about RN v8 2020-05-21 23:37:07 +03:00
Hadar Rottenberg
213a14b0c8 add: better RN docs 2020-05-21 23:26:20 +03:00
Hadar Rottenberg
d69e6d43fe fix: make import isomorphic (this breaks EXPO) 2020-05-21 23:26:10 +03:00
Hadar Rottenberg
99cb0e92df add: lates ras.js 2020-05-21 22:10:07 +03:00
Hadar Rottenberg
d681c5ca91 Merge remote-tracking branch 'origin/master' into pr/sirpy/12 2020-05-21 22:05:19 +03:00
Hadar Rottenberg
5372279fb2 add: asyncstorage for rad 2020-05-16 23:21:29 +03:00
Mark Nadal
2501b5cdea
Update CHANGELOG.md 2020-05-16 04:18:23 -07:00
Mark Nadal
116d485379
Merge pull request #878 from rogowski/bug-121
Test file for bug-121
2020-05-16 03:19:45 -07:00
Mark Nadal
c7f017191e
Merge pull request #934 from estebanrfp/master
added vanilla js version of user.html
2020-05-16 01:57:16 -07:00
Mark Nadal
0af7379ed6
Merge pull request #939 from revolunet/patch-1
fix: fix #918
2020-05-16 01:56:12 -07:00
Mark Nadal
9f92b014f4
Merge pull request #946 from amark/dev
Fix 2020 "sync" issue for @JamieRez
2020-05-16 01:42:28 -07:00
Mark Nadal
eb8985b2fd vbump 2020-05-16 01:39:18 -07:00
Mark Nadal
49bc20ee95 nodejs fixes, some tmp 2020-05-16 01:33:41 -07:00
Mark Nadal
880947afe8 fix cache miss of cache hit 2020-05-16 01:23:40 -07:00
Mark Nadal
9d03ce83d0
Update README.md 2020-05-10 21:24:48 -07:00
Julien Bouquillon
284a028e1c
fix: fix #918
only replace http if at the start of the url
2020-05-04 01:22:18 +02:00
Mark Nadal
5a430bc812 stats should not swallow errors 2020-05-02 18:05:51 -07:00
Mark Nadal
985cfa2b4d build/unbuild 2020-05-01 19:13:13 -07:00
Mark Nadal
51be588a74 nomem for Meething! 2020-05-01 19:08:25 -07:00
Mark Nadal
c93305ae11 fix ws noServer 2020-05-01 18:54:16 -07:00
Mark Nadal
ff99a4639d
Merge pull request #936 from joedakroub/master
Fix for ES Modules when using a peer
2020-04-24 11:50:36 -07:00
rogowski
06a4a0bfeb
Merge pull request #868 from rogowski/bug-850
Test for bug-850 - Saving a deep object under a user node seems to lose public key at some depth. Add test.
2020-04-23 21:45:06 -03:00
Mark Nadal
29d596baf7 Merge branch 'master' of http://github.com/amark/gun 2020-04-22 14:31:54 -07:00
Mark Nadal
018fa8cc1e FIX user.set({obj not secure, + try fix hash & predictable souls + beta forgetfulness 2020-04-22 14:31:27 -07:00
estebanrfp
1d85346770 replaced gun peers url to location.origin 2020-04-20 21:15:36 +02:00
estebanrfp
f8e2230b04 added vanilla js version of user.html 2020-04-20 20:30:30 +02:00
estebanrfp
550b0633b0 Merge branch 'master' of github.com:amark/gun 2020-04-19 21:15:13 +02:00
Mark Nadal
5bedffc864
Merge pull request #930 from lmangani/patch-1
Allow externally provided websockets in constructor
2020-04-19 06:46:58 -07:00
Lorenzo Mangani
4124a2db50
Allow externally provided websockets in constructor
For consideration (this can also be overloaded in stock gun)
The current ws.js code does not allow providing an external websocket while initializing Gun. This one line patch makes it possible:

```
        var myWS= new WebSocket.Server({ noServer: true, path: pathname});
        var myGun = new Gun({ 
            ws: { noServer: true, path: '/mygun', web: myWS }, 
            web: myWS 
        });
```
2020-04-16 21:54:02 +02:00
Septs
47b29768c7 chore: reduce docker image 2020-04-14 17:21:34 +08:00
Joe Dakroub
1a71145b4d
Merge pull request #1 from joedakroub/jd/gun-mesh-this-error-esm-fix
Test fix - Loading gun via ES Modules throws error when setting a peer
2020-04-10 11:03:25 -04:00
joedakroub
46b0fb6dc5 Added this check on ine 2151 to prevent an error when using ESM 2020-04-10 10:48:01 -04:00
Mark Nadal
17f90d6d10
Merge pull request #927 from genderev/master
Explains Unubo issue
2020-04-10 02:17:59 -07:00
genderev
b0de4027e3
Update README.md 2020-04-10 01:44:09 +00:00
estebanrfp
f0b041cdb1 improve indentation code - vanilla folder examples 2020-04-08 18:13:33 +02:00
estebanrfp
964b1c9fcf +indentation 2020-04-07 19:17:00 +02:00
estebanrfp
60a62d867c update todo.html - keep it simple 2020-04-07 18:55:57 +02:00
Mark Nadal
ce78d7a4c9
Merge pull request #924 from estebanrfp/patch-1
Create screen.html - vanilla javascript version
2020-04-06 21:36:36 -07:00
Mark Nadal
2f34d8cb4e
Merge pull request #906 from jussiry/patch-1
react todo example: fixes input clear on submit
2020-04-06 21:35:06 -07:00
Mark Nadal
e2f4f11e6a
Merge pull request #923 from smpsnr/fix-private-example
update private.html to fix #808
2020-04-06 21:29:21 -07:00
Esteban Fuster Pozzi
31abc3eaa0
Create screen.html
Vanilla javascript version, video streaming using Gun
2020-04-06 23:33:09 +02:00
sam
6a3c55cc69 update private.html to fix #808 2020-04-06 15:07:46 -04:00
Mark Nadal
47e7c495d7 up not found ack count before new soul + use more predictable offline souls + content address hash fix 2020-04-06 01:34:41 -07:00
Mark Nadal
87955288a0
Merge pull request #921 from amark/dev
Dev
2020-04-06 00:30:34 -07:00
Mark Nadal
fc58d3552a important offline fixes & assumption/default changes 2020-04-05 17:41:51 -07:00
Mark Nadal
ce865da613
Merge pull request #916 from rayros/master
Fix: Angular - Cannot find module 'ts-type'
2020-04-05 17:19:16 -07:00
Mark Nadal
879cd84ccc
Merge pull request #920 from brysgo/patch-1
clean up subscription when component unmounts
2020-04-05 17:17:23 -07:00
Bryan Goldstein
8ec8e394b8
clean up subscription when component unmounts 2020-04-04 19:45:47 -04:00
Mark Nadal
156b84acee
Create FUNDING.yml 2020-04-04 16:15:49 -07:00
Mark Nadal
ec8808735b Merge branch 'master' into dev 2020-04-02 13:10:48 -07:00
Mark Nadal
7857c70b6e
Create yson.js 2020-03-31 23:19:36 -07:00
Hadar Rottenberg
22a76f489d Merge branch 'amark-master' 2020-03-30 15:59:55 +03:00
Hadar Rottenberg
3520f04b36 Merge branch 'master' of https://github.com/amark/gun into amark-master 2020-03-30 15:57:08 +03:00
rayros
7ba9aa1c3d remove ts-type devdeps 2020-03-29 20:46:14 +02:00
rayros
cba32c1040 remove ts-type import 2020-03-29 20:45:57 +02:00
Mark Nadal
12ff85aa2f
Merge pull request #914 from jussiry/dev
meta cmd hold, backspace etc.
2020-03-22 13:09:14 -07:00
Jussi Rytkönen
28205fe69e
Merge branch 'dev' into dev 2020-03-22 22:01:37 +02:00
Jussi Rytkönen
ee7a01675f meta cmd hold, backspace etc. 2020-03-22 21:38:12 +02:00
Mark Nadal
54a50d62ff
Merge pull request #879 from rogowski/bug-783
Test file for bug-783
2020-03-18 12:06:58 -07:00
Mark Nadal
dec75be45e
Merge pull request #888 from rogowski/bug-686
Test for bug-686: Cannot put string 'null' as value in put. SEA error…
2020-03-18 12:05:50 -07:00
Mark Nadal
1b002bd49d
Merge pull request #884 from rogowski/bug-322
Test for bug-322: modify returned node in callback leaks thru
2020-03-18 12:04:31 -07:00
sirpy
6d05150ae8
fix: remove undefined/unused variable 2020-03-18 09:23:48 +02:00
Mark Nadal
fbcb369aa8
Merge pull request #908 from amark/manhattan
Manhattan
2020-03-06 02:03:41 -08:00
Mark Nadal
6bdc99da1a Merge branch 'master' into manhattan 2020-03-06 01:49:31 -08:00
Mark Nadal
4163687acb check if migration needed 2020-03-06 01:35:14 -08:00
Mark Nadal
9de7d0f38d fix chain emit & faith 2020-03-04 22:58:25 -08:00
Mark Nadal
da7c243fb0 rad check node read, important puffs 2020-03-04 02:18:49 -08:00
Mark Nadal
97065b70fa in s not ms 2020-03-03 11:27:45 -08:00
Mark Nadal
b4568e52c3 prof back in 2020-03-03 11:25:23 -08:00
Mark Nadal
c58e412208 puff it 2020-03-03 04:24:54 -08:00
Mark Nadal
2540818665 don't lose ref 2020-03-02 15:26:26 -08:00
Mark Nadal
ccf2c303b9 puff say loop? see how it goes. 2020-03-02 15:03:18 -08:00
Mark Nadal
3733473115 perf notes 2020-03-02 13:14:04 -08:00
Mark Nadal
6f053b6327 restore 2020-02-27 19:45:32 -08:00
Mark Nadal
5a70f506d3 TEMPORARILY disable RAD 2020-02-27 19:40:21 -08:00
Mark Nadal
8f78669997 must be defined undefined 2020-02-25 12:34:47 -08:00
Mark Nadal
e5c06e7491 put debug (dbg) 2020-02-25 12:29:28 -08:00
Mark Nadal
74f6638227 DO NOT PUBLISH! ECHO TEST. 2020-02-25 03:32:42 -08:00
Mark Nadal
9a3225e387 file serve stats 2020-02-25 02:31:34 -08:00
Mark Nadal
105d81f7a6 undo temporary test 2020-02-21 23:53:02 -08:00
Mark Nadal
5bf425d2c4 TEMPORARY test 2020-02-21 23:32:37 -08:00
Mark Nadal
79fac4cb45 fix large records saving to RAD 2020-02-21 22:39:06 -08:00
Mark Nadal
50a7373d00 woops, what? 2020-02-21 19:46:25 -08:00
Mark Nadal
1c29092af8 -log +stat, not gets or dbg yet tho 2020-02-21 16:38:26 -08:00
Mark Nadal
ff04e23b64 fix .pub on account check 2020-02-19 11:47:26 -08:00
Jussi Rytkönen
c7f0acb0df
react todo example: fixes input clear on submit 2020-02-19 14:37:44 +02:00
Mark Nadal
3ab14ffc42 3rd rewrite, have batched acks. 2020-02-17 12:38:46 -08:00
Mark Nadal
699823d578 if emitting chain perf fault, then redo _rad 2020-02-13 00:53:42 -08:00
Mark Nadal
c785a184c1 yet again 2020-02-12 14:32:45 -08:00
Mark Nadal
63d8114791 I want the count 2020-02-12 14:07:27 -08:00
Mark Nadal
f84af3fcd7 more info 2020-02-12 13:56:48 -08:00
Mark Nadal
cac59fbc12 ugh lol finally get this right? 2020-02-12 13:18:06 -08:00
Mark Nadal
fb0e44f4a6 Update gun.js 2020-02-12 13:00:55 -08:00
Hadar Rottenberg
947ee8455c add: update readme with example 2020-02-12 21:25:58 +02:00
Hadar Rottenberg
5016298b45 fix: typo 2020-02-12 21:17:15 +02:00
Hadar Rottenberg
72c699f20b add: restore old crypto package, isomorphic changes things in react-native 2020-02-12 21:07:24 +02:00
Mark Nadal
77263e40b7 allow sort / dynamic prefix on hash? 2020-02-12 11:06:02 -08:00
Mark Nadal
3085579915 check mix time 2020-02-12 09:46:37 -08:00
Mark Nadal
35414daa20 better diagnostics 2020-02-12 09:19:18 -08:00
Mark Nadal
4aadd43248 woops put was too big for email 2020-02-12 07:06:08 -08:00
Mark Nadal
a3383e5617 FIX CHAIN EMIT / ON, add more stats 2020-02-12 06:35:17 -08:00
sirpy
40b47a2621
Update README.md 2020-02-12 10:32:35 +02:00
sirpy
d15721c46d
Update README.md 2020-02-12 10:30:26 +02:00
Hadar Rottenberg
5a1f8350cb fix: match amark master 2020-02-12 10:27:00 +02:00
Hadar Rottenberg
e355d41f60 refactor: remove duplicate 2020-02-12 10:22:36 +02:00
Hadar Rottenberg
ca4b5ae016 Merge remote-tracking branch 'amark/master' 2020-02-12 10:03:51 +02:00
Hadar Rottenberg
a72342431f add: update to use webview-crypto 2020-02-12 09:54:04 +02:00
Mark Nadal
82d8192700 woops that was crashing stuff 2020-02-11 20:38:59 -08:00
Mark Nadal
5253e2084a only check once per disk, not each cb of it 2020-02-11 16:01:42 -08:00
Mark Nadal
c4fc859789
Merge pull request #898 from bluelovers/master
refactor: update types for better extendable
2020-02-11 15:05:08 -08:00
Mark Nadal
4137c52598
Merge pull request #902 from bluelovers/pr/003
fix: bug when import 'lib/then'
2020-02-11 14:52:55 -08:00
Mark Nadal
2801198691
Merge pull request #899 from bluelovers/pr/002
chore: fix log
2020-02-11 14:51:36 -08:00
Mark Nadal
3668f5099f uptime decimal 2020-02-11 14:32:25 -08:00
Hadar Rottenberg
1f3b090f81 add: update dependencies 2020-02-11 10:28:37 +02:00
Mark Nadal
77fda9d42c Update radisk.js 2020-02-10 23:17:15 -08:00
Mark Nadal
ed82e28109 Update rs3.js 2020-02-10 18:50:01 -08:00
Mark Nadal
3c30c52f01 work! 2020-02-10 18:34:29 -08:00
Mark Nadal
d075423ffb crash it 2020-02-10 18:32:15 -08:00
Mark Nadal
48b288280d Update sea.js 2020-02-10 18:25:01 -08:00
Mark Nadal
b45be18ad3 Update sea.js 2020-02-10 18:11:23 -08:00
Mark Nadal
58f8cd0e03 find ugh 2020-02-10 18:07:48 -08:00
Mark Nadal
b101b492d4 duh 2020-02-10 17:58:32 -08:00
Mark Nadal
bc40109e61 fixup 2020-02-10 17:48:39 -08:00
Mark Nadal
d6dae4871e p v b 2020-02-10 15:22:07 -08:00
Mark Nadal
0936c326f8 unit tests passing 2020-02-10 15:13:37 -08:00
Hadar Rottenberg
6f65c6ef26 Merge branch 'amark-master' 2020-02-09 22:28:58 +02:00
Hadar Rottenberg
fd26e39acc Merge branch 'master' of https://github.com/amark/gun into amark-master 2020-02-09 22:28:42 +02:00
Mark Nadal
3127b41f38 full circuit 2020-02-08 02:02:36 -08:00
Mark Nadal
9b188dea72 Update .gitignore 2020-02-06 14:11:47 -08:00
Mark Nadal
e614100c47 Merge branch 'dev' into manhattan 2020-02-06 14:11:39 -08:00
Mark Nadal
e98e51a0ea Merge branch 'master' into manhattan 2020-02-06 14:09:37 -08:00
Mark Nadal
7cf2d42dcd yet another day. 2020-02-06 14:05:27 -08:00
bluelovers
c59e0e95f9 fix: type CryptoKeyPair and rename to IGunCryptoKeyPair 2020-02-06 21:41:12 +08:00
bluelovers
273f7fce8d chore: update type Gun.log 2020-02-06 21:41:11 +08:00
bluelovers
16f911e126 fix: bug when import 'lib/then' 2020-02-06 20:54:42 +08:00
Mark Nadal
89b8f01cda because yeah! 2020-02-05 01:57:11 -08:00
bluelovers
d2512f2842 chore: fix log 2020-02-05 11:00:16 +08:00
bluelovers
90ca38ee7e chore: yarn-tool lockfile --npm --overwrite 2020-02-05 09:32:00 +08:00
bluelovers
4a42a4f1ce refactor: split types node , SEA 2020-02-05 09:24:50 +08:00
bluelovers
4d252a96f7 fix: types chain.then 2020-02-05 09:02:32 +08:00
bluelovers
4e94d7f04e chore: .npmignore 2020-02-05 08:38:51 +08:00
bluelovers
c93149a3a0 refactor: update types for better extendable 2020-02-05 08:37:00 +08:00
bluelovers
248e09c573 split @types/gun 2020-02-05 08:06:20 +08:00
bluelovers
82d7bfad61 copy @types/gun 2020-02-05 07:43:31 +08:00
Mark Nadal
60c98a5ba0
Update install.sh 2020-02-04 02:45:18 -08:00
Mark Nadal
fa8a63606c TODO SPAM, REMEMBER TO UNCOMMIT 2020-02-03 14:26:01 -08:00
Mark Nadal
efd055c2b0 notes & tests 2020-02-03 14:25:41 -08:00
Mark Nadal
827197dbb7 only flamegraph emails 2020-02-03 10:27:49 -08:00
Mark Nadal
a5a0eab24c Update trace.js 2020-02-02 22:59:48 -08:00
Mark Nadal
782750d728 Update trace.js 2020-02-02 22:57:06 -08:00
Mark Nadal
a980f4efc1 ? trace ? tmp 2020-02-02 22:30:13 -08:00
Mark Nadal
75148ec540 Update Procfile 2020-02-02 10:33:37 -08:00
Mark Nadal
d8f3ed7edb Update Procfile 2020-02-02 10:05:10 -08:00
Mark Nadal
c97526a36c Update Procfile 2020-02-02 09:40:59 -08:00
Mark Nadal
cae8264832 Update Procfile 2020-02-02 09:40:49 -08:00
Mark Nadal
6281fed309 Update Aptfile 2020-02-02 09:31:26 -08:00
Mark Nadal
ebfd82eac6 Update Aptfile 2020-02-02 09:27:51 -08:00
Mark Nadal
cc6415fa44 Update Aptfile 2020-02-02 09:24:37 -08:00
Mark Nadal
c5d3ef47e7 try live debug (chrome inspector) 2020-02-02 09:17:43 -08:00
Mark Nadal
d3e89c2adc live debug ? 2020-02-02 01:13:47 -08:00
Mark Nadal
328ae52c2c begin 2020-02-02 01:09:48 -08:00
Mark Nadal
9dfdf608dc skip saving in-mem acks? thrash socket 0? 2020-01-31 05:41:26 -08:00
Mark Nadal
e6b61f667e how long is 1ms in actuality? 2020-01-30 20:36:22 -08:00
Mark Nadal
56488b567e
Merge pull request #897 from jabis/master
webrtc defaults messing at least firefox
2020-01-30 12:44:26 -08:00
Jabis Sevon
bf907d5f87
Merge pull request #1 from jabis/jabis-patch-1
Update webrtc.js
2020-01-31 03:34:18 +07:00
Jabis Sevon
b3f87b180e
Update webrtc.js
The above change corrects at least firefox RTC Peer handler where it **throws** on over 6 ice servers, and updates url: to urls: removing spec deprecation warnings

Left todo: when ICE Fails it throws, and all `.on` and `.open` listeners will fail
2020-01-31 03:32:56 +07:00
Mark Nadal
9dbbf856d3
Declare CDN 2020-01-29 21:42:24 -08:00
Mark Nadal
66361cb6f3
Update README.md 2020-01-29 20:33:27 -08:00
Mark Nadal
fa5fe8a325 refine health 2020-01-29 19:05:52 -08:00
Mark Nadal
6593844ed0 IF email ENV CONFIG set, report slows 2020-01-28 21:36:39 -08:00
Mark Nadal
958d736e0c IF email ENV CONFIG set, report slow parse 2020-01-28 17:15:50 -08:00
Mark Nadal
faa776a8e5 cache remote files, quick ugly test experiment? 2020-01-24 12:02:17 -08:00
Mark Nadal
39d467314a skip lex cursors until fixed 2020-01-24 10:37:52 -08:00
Martti Malmi
9890fb88aa tests for cursor and descending ordering 2020-01-24 15:33:45 +02:00
Martti Malmi
ff9ff71cb2 multicast fix 2020-01-24 15:29:30 +02:00
Mark Nadal
fe3a25682a
Merge pull request #889 from amark/dev
woops THIS is CLOSER to the BIG 2020 RELEASE, don't use old one :P
2020-01-22 23:21:09 -08:00
Mark Nadal
8f874d6f32 try relocate mislocated data on reads 2020-01-22 22:39:02 -08:00
Mark Nadal
ba2b207dd6 hopefully increases limits? 2020-01-22 06:56:57 -08:00
Adriano Rogowski
9f446a08d4 Test for bug-686: Cannot put string 'null' as value in put. SEA error thrown 2020-01-21 20:46:13 -03:00
Mark Nadal
b5fd757db4 dev -> master, master -> 2020-01-21 15:38:31 -08:00
Adriano Rogowski
de85106c42 Test for bug-322: modify returned node in callback leaks thru 2020-01-21 20:37:36 -03:00
Mark Nadal
f8f9cddafe
Merge pull request #882 from amark/dev
THE 2020 MAJOR RELEASE!
2020-01-21 14:55:38 -08:00
Mark Nadal
5c4126bc0a done 2020-01-21 09:34:02 -08:00
Mark Nadal
dc94252f20 crash while deleting s3 2020-01-21 09:32:00 -08:00
Mark Nadal
08fc562eac don't rebuild file index (?) 2020-01-21 00:37:00 -08:00
Mark Nadal
80159888fb max defer timeout, + tmps 2020-01-20 23:43:44 -08:00
Mark Nadal
3d8c8a2f47 don't delete message tracking too soon 2020-01-20 14:20:25 -08:00
Mark Nadal
23420c9f60
Retiring this example due to old security leak of mine that got copy&pasted
Please see https://github.com/amark/gun/issues/880
2020-01-20 09:39:12 -08:00
Mark Nadal
ecf4fda285 Only track msg meta data, renew original request on @, fix faith/raw/rad unique, better GC, GC tracker too. 2020-01-18 17:49:44 -08:00
Mark Nadal
06099bf881 faith must be unique, store glue graph must be reset, try evict on v8 heap limit 2020-01-18 00:30:16 -08:00
Adriano Rogowski
277147acc0 Test file for bug-783 2020-01-14 22:03:54 -03:00
Adriano Rogowski
86c74737f5 Test file for bug-121 2020-01-14 21:59:57 -03:00
Mark Nadal
50ee5c766f
Update README.md 2020-01-14 11:09:47 -08:00
Mark Nadal
db868d0e3a security sticky note ALWAYS has to be unique, CRITICAL 7 letter fix 2020-01-13 13:08:37 -08:00
Hadar Rottenberg
dab91974c1 add: decrypt opposite of secret 2020-01-13 13:00:51 +02:00
Hadar Rottenberg
761030d721 Merge branch 'pr/sirpy/9' 2020-01-13 12:52:39 +02:00
Hadar Rottenberg
af43b394c3 Merge branch 'master' into pr/sirpy/9 2020-01-13 12:49:57 +02:00
Mark Nadal
77be32167f moving forward 2020-01-11 04:39:19 -08:00
Mark Nadal
3547039cba tmp then revert: force s3 list command 2020-01-11 04:25:06 -08:00
Mark Nadal
8d5d6a3ee6 make rad great again, replace file after 2nd half saved already, check for no files, thanks @mhelander 2020-01-11 04:23:33 -08:00
Mark Nadal
08c45c08d4 delete logs 2020-01-10 06:15:05 -08:00
Mark Nadal
e8b5587b99 HNPERF performance tests 2020-01-10 06:07:31 -08:00
Mark Nadal
bbf7791893
Merge pull request #874 from GoodDollar/rnsupport
Rnsupport
2020-01-09 11:58:09 -08:00
Mark Nadal
47b0519117 tmp 2020-01-08 14:31:17 -08:00
Mark Nadal
787ea543b4 tmp 2020-01-07 20:13:51 -08:00
Mark Nadal
ccea6a030c change DAM configs, don't send @ ACK if can't find via trace. 2020-01-07 20:07:34 -08:00
Mark Nadal
5d7e1dd226 tmp 2020-01-07 18:57:59 -08:00
Mark Nadal
a3784d7930 tmp 2020-01-07 18:41:38 -08:00
Mark Nadal
05ba89888f tmp 2020-01-07 18:18:35 -08:00
Mark Nadal
2a450bf3cc tmp 2020-01-07 18:16:32 -08:00
Mark Nadal
e4b4611856 tmp 2020-01-07 17:57:30 -08:00
Mark Nadal
6eab251438 tmp 2020-01-07 17:35:45 -08:00
Hadar Rottenberg
0c27b81993 Merge branch 'rnsupport' of https://github.com/gooddollar/gun into rnsupport 2020-01-07 15:32:30 +02:00
Hadar Rottenberg
f287887e87 fix: force use of isomorphiccrypto on react-native 2020-01-07 15:32:22 +02:00
sirpy
7a08fc400a
Merge branch 'master' into rnsupport 2020-01-07 15:29:51 +02:00
Nicolas Castellanos
de43978382
Merge pull request #8 from GoodDollar/rnsupport-no-scolons
Remove invalid semicolons
2020-01-06 15:48:01 -03:00
sirpy
4b14e7c645
remove semicolons (#7)
Co-authored-by: Nicolas Castellanos <imnicastelo@gmail.com>
2020-01-06 18:08:47 +02:00
Nicolás Castellanos
fcce25df48 remove semicolons 2020-01-06 12:16:17 -03:00
Hadar Rottenberg
4619e57968 fix: unknown issue where global S object is modified, then sign doesnt work 2020-01-06 15:34:58 +02:00
Adriano Rogowski
c7dcbfbd7e test sea - User\'s nodes must be signed when on user scope! issue #850 issue #616 2020-01-05 21:13:44 -03:00
Adriano Rogowski
d9d9e5c5d0 sea - correct path to sea test. 2020-01-05 21:13:28 -03:00
Mark Nadal
4bb9d2dd51 re-enable all stats for demo/testing peers 2019-12-30 12:26:22 -08:00
Mark Nadal
14ebd73d80 bump version 2019-12-30 12:16:45 -08:00
Mark Nadal
8f5ab18fa3
Merge pull request #860 from amark/dev
Dev
2019-12-30 12:06:05 -08:00
Mark Nadal
88a5c8fea0 .open / .load opt meta , axe mob , music -> wave 2019-12-30 11:57:28 -08:00
Mark Nadal
9db8adea2a disconnect duplicate connections thanks to @malsaeed 2019-12-20 15:08:36 -08:00
Mark Nadal
46066fee0d https://gun.eco/docs/DAM#self 2019-12-19 16:08:37 -08:00
Mark Nadal
9caf77dd0c
Update todo.html 2019-12-16 16:32:16 -08:00
Mark Nadal
fc00ed0239
Merge pull request #853 from amark/dev
Dev
2019-12-12 16:28:22 -08:00
Mark Nadal
dedab599d6 micro adjustment for calling a 2nd .once( with undefined 2019-12-12 16:19:34 -08:00
Mark Nadal
8dbd9db17b testing 2019-12-11 11:19:21 -08:00
Mark Nadal
bc87faa8d9 tweak doc 2019-12-10 03:42:09 -08:00
sirpy
512d2c8c32
add: wait for once, so data is returned on first get (#6)
* add: wait for once, so data is returned on first get

* Update then.js
2019-12-08 17:49:48 +02:00
sirpy
29ed57e955
react native support (#5)
* WIP: use isomorphic-webcrypto for RN

* WIP: expo friendly exports

* WIP: buffer for RN

* WIP: textencoder for RN

* WIP: aeskey from jwk instead of raw for RN

* fix: missing taglength for msrcrypto

* add: update isomorphic-webcrypto

* add: match isocrypto api

* add: failing test for 4e2 as 400

* fix: remove extra atob btoa

* add: sea unbuild

* refactor: according to mhelander review

* refactor: shorter require

* fix: make bug test pass, capitlize bug test.

* fix: npm ci + sea test

* add: restore old browser export and add react-native friendly exports

* fix: import buffer into correct global/window

* fix: bad comparison

* fix: only include buffer if no btoa/atob
2019-12-08 17:41:26 +02:00
sirpy
af1590cf60
Merge pull request #4 from amark/master
update to amark/gun master
2019-12-08 17:40:24 +02:00
Mark Nadal
1cd0c08ab0 axe doc music dht :) 2019-12-06 16:24:12 -08:00
Hadar Rottenberg
b2709e2eb7 fix: only include buffer if no btoa/atob 2019-11-29 11:42:47 +02:00
Mark Nadal
d2c4e7c8e8 if wifi off don't crash due to multicast membership 2019-11-23 12:09:02 -08:00
Mark Nadal
1a4a621fd1
Delete tmp.test.sql.js 2019-11-22 23:16:56 -08:00
Mark Nadal
14b1ea1896 Update 4dht.js 2019-11-22 17:59:47 -08:00
Mark Nadal
c1c7c9ec74
Update README.md 2019-11-22 16:15:26 -08:00
Mark Nadal
eece7f91ac merge with @jussiry + correct HUD display 2019-11-22 14:20:09 -08:00
Mark Nadal
3a0b084449
Merge pull request #846 from jussiry/dev
click open, mouse button drag, open cancel disabled
2019-11-22 13:58:39 -08:00
Jussi Rytkönen
b41022ca91 click open, mouse button drag, open canceld disabled 2019-11-22 17:09:29 +02:00
Mark Nadal
79216341e3 docs tweaks 2019-11-22 03:13:40 -08:00
Mark Nadal
b57a7a8013 Update meta.js 2019-11-22 03:10:54 -08:00
Mark Nadal
22547099bd don't cancel user input on meta HUD inputs 2019-11-21 20:49:51 -08:00
Mark Nadal
24aec9f466
Update README.md 2019-11-21 15:23:23 -08:00
Mark Nadal
5a12e30996 clean up 2019-11-21 15:20:39 -08:00
Mark Nadal
48ebe38e08 Create todo.html 2019-11-21 15:17:19 -08:00
Mark Nadal
3f3e973f39
Merge pull request #845 from amark/dev
fix for @capitalistdog @danlugo92
2019-11-21 00:37:31 -08:00
Mark Nadal
5aafbbbff0 version bump to publish fix for fix for @capitalistdog @danlugo92 2019-11-21 00:28:48 -08:00
Mark Nadal
7ac8e29f67 fix for @capitalistdog @danlugo92 + docs & game proto alpha 2019-11-21 00:09:59 -08:00
Hadar Rottenberg
ef49a73e4a fix: bad comparison 2019-11-21 09:12:56 +02:00
Hadar Rottenberg
5172c38913 fix: import buffer into correct global/window 2019-11-21 08:29:40 +02:00
Mark Nadal
21fcdf8693 Merge branch 'dev' of http://github.com/amark/gun into dev 2019-11-20 03:50:09 -08:00
Mark Nadal
79bd5b9827
Merge pull request #843 from jussiry/dev
meta.js alpha version of tap only
2019-11-20 02:44:49 -08:00
Jussi Rytkönen
8dbe77f4d0 meta.js alpha version of tap only 2019-11-20 12:31:51 +02:00
Mark Nadal
56678aa7f5 Merge branch 'dev' of http://github.com/amark/gun into dev 2019-11-17 03:35:25 -08:00
Mark Nadal
e048b897da stuff 2019-11-17 03:35:12 -08:00
Mark Nadal
875de844fb
Merge pull request #838 from Dletta/patch-1
Added missing reference for global obj
2019-11-13 05:46:15 -08:00
Mark Nadal
1895010188
Merge pull request #840 from jussiry/dev
Dev
2019-11-13 01:32:59 -08:00
Jussi Rytkönen
5a58f77a32 meta.js update from edide 2019-11-11 17:38:56 +02:00
Jussi Rytkönen
301f8910fb meta.js update from edide 2019-11-11 17:33:22 +02:00
Mark Nadal
c99ad39c56 Update README.md 2019-11-11 15:27:10 +02:00
Mark Nadal
0a02e7bfaa Update README.md 2019-11-11 15:27:10 +02:00
Mark Nadal
1c3ea91011
Update README.md 2019-11-09 15:25:23 -08:00
Mark Nadal
8dc8b27cd0
Update README.md 2019-11-09 15:22:06 -08:00
Mark Nadal
8da4689fea paste.html ! 2019-11-09 15:02:47 -08:00
Mark Nadal
1ba73dd47b update basics 2019-11-09 14:56:16 -08:00
Mark Nadal
f8bb17e27f paste example 2019-11-09 14:38:02 -08:00
Mark Nadal
b5b4357a5d screen recorder thanks to @JamieRez 2019-11-08 14:58:59 -08:00
Mark Nadal
fa724b5abb test 2019-11-08 08:51:17 -08:00
Jachen Duschletta
ef4b4ff795
Added missing reference for global obj 2019-11-07 13:45:32 -08:00
Mark Nadal
8dc043c44f
Merge pull request #837 from jussiry/master
meta.js ununbound
2019-11-04 15:50:42 -08:00
Jussi Rytkönen
c0224c4d28
meta.js ununbound 2019-11-05 01:02:09 +02:00
Mark Nadal
7c6886857c
Merge pull request #836 from jussiry/master
meta.js modularization
2019-11-04 12:31:58 -08:00
Jussi Rytkönen
42926ec09d meta/edit removed from gitignore 2019-11-04 22:28:23 +02:00
Hadar Rottenberg
325c7b48ed add: restore old browser export and add react-native friendly exports 2019-11-04 13:02:11 +02:00
Hadar Rottenberg
92a4f7ad24 Merge branch 'master' into rnsupport 2019-11-04 12:49:25 +02:00
sirpy
f15281dcd0
Merge pull request #3 from amark/master
update to master
2019-11-04 12:49:00 +02:00
Hadar Rottenberg
209fe60c76 fix: npm ci + sea test 2019-11-04 12:41:43 +02:00
sirpy
e8f15b27e3
fix: make bug test pass, capitlize bug test. 2019-11-04 12:34:03 +02:00
Mark Nadal
1c6d519162
Merge pull request #832 from Dletta/dev
Overview Stats
2019-10-29 11:40:09 -07:00
Jussi Rytkönen
e92bf7b940 modularizes meta.js 2019-10-29 18:06:39 +02:00
Dletta
b916aea3c1 removed neon tube font and made the next best thing, green stuff 2019-10-28 19:57:32 -07:00
Mark Nadal
d69c315023
Merge pull request #833 from BillyNate/readmedeployunebo
Add Unebo as a deploy option
2019-10-28 14:24:10 -07:00
Dletta
d193a332bf Second Iteration
dynamic keys and lines
2019-10-26 16:30:50 -07:00
Dletta
528db20161 Overview Stats
First Draft, non-auto loop version
2019-10-25 23:10:14 -07:00
Mark Nadal
026b278311 temporary push for @Dletta to update stats 2019-10-25 15:44:07 -07:00
Nate
0050eeb89a
Added Unebo to the 'deploy' section of the readme 2019-10-25 23:11:09 +02:00
Martti Malmi
2b82d6211f
Merge pull request #830 from BillyNate/fixw10multicast
Multicast fix for Windows 10
2019-10-24 12:47:05 +03:00
BillyNate
76a5231498 Fixed an error on Windows 10 in multicasting
Fixes #787
2019-10-22 23:25:48 +02:00
Mark Nadal
418b0b22b6
Update README.md 2019-10-22 11:41:23 -07:00
Mark Nadal
d387a4f790 drop chat example until DOM bug fixed 2019-09-30 14:26:55 -07:00
Mark Nadal
e997b4d2d1 unbuild & delete plugins changing defaults 2019-09-30 14:06:29 -07:00
Mark Nadal
4ffcc5b17a Merge branch 'master' of http://github.com/amark/gun 2019-09-30 13:36:47 -07:00
Mark Nadal
df272501c0 update panic test 2019-09-30 13:36:43 -07:00
Mark Nadal
d7de5e7a67
Merge pull request #825 from Dletta/patch-1
Added better error handling into put
2019-09-30 11:29:48 -07:00
Mark Nadal
b40a6a50fc Create 3puts.js 2019-09-30 01:13:13 -07:00
Mark Nadal
247ddee578 temporary fix for logs 2019-09-30 01:12:57 -07:00
Mark Nadal
b3399a4c01
Merge pull request #826 from amark/dev
Dev
2019-09-30 00:53:38 -07:00
Jachen Duschletta
6f2c246146
Added better error handling into put 2019-09-28 20:59:35 -07:00
Mark Nadal
4de57d4014 reduce RAD chunk and try JSON serializing 2019-09-27 02:22:20 -07:00
Mark Nadal
382cbc4798 does this work? don't read if already read? 2019-09-27 01:52:35 -07:00
Hadar
a2086cd09c refactor: shorter require 2019-09-22 00:25:15 +03:00
Hadar
8e7a5369cc refactor: according to mhelander review 2019-09-21 23:09:32 +03:00
Mark Nadal
814d330b8a more logs 2019-09-20 00:55:09 -07:00
Mark Nadal
7cd19b3d67 debug info 2019-09-19 17:00:16 -07:00
Mark Nadal
93cd24caa7
Update bye.js 2019-09-19 05:00:28 -07:00
Hadar
a6fc7fd5c5 add: sea unbuild 2019-09-18 00:22:10 +03:00
Hadar
0804c5ef24 fix: remove extra atob btoa 2019-09-18 00:19:34 +03:00
Hadar
6d798a136f Merge branch 'master' into rnsupport 2019-09-17 23:41:30 +03:00
sirpy
87f69aebc9
Merge pull request #2 from amark/master
update to gun master
2019-09-17 23:37:16 +03:00
Mark Nadal
14f6ba098a
Update README.md 2019-09-17 08:26:42 -07:00
Mark Nadal
ef29f181fe Merge branch 'master' into dev 2019-09-17 03:16:33 -07:00
Mark Nadal
1f0d0d62b0 does puff help? 2019-09-17 03:15:13 -07:00
Mark Nadal
68f4308664 loopit 2019-09-17 01:53:56 -07:00
Mark Nadal
f4526238f3 add more stats 2019-09-17 00:27:39 -07:00
Mark Nadal
3953c17130
Merge pull request #818 from amark/master
merge master INTO dev
2019-09-16 18:12:55 -07:00
Mark Nadal
99ed43f5c4 console.debug -> console.only & unbuild 2019-09-16 13:59:44 -07:00
Mark Nadal
4fb04b8f53 base64 shim 2019-09-16 12:36:18 -07:00
Mark Nadal
89578efd68
Merge pull request #814 from mboperator/sea-switch-to-webcrypto
[SEA] Migrate from `node-webcrypto-ossl` to `@peculiar/webcrypto`
2019-09-16 10:09:04 -07:00
Mark Nadal
12d61ace2c
DROPPING NODEJS 8 SUPPORT!!!! 2019-09-16 10:04:38 -07:00
Hadar
932ff99c58 add: failing test for 4e2 as 400 2019-09-15 01:14:18 +03:00
Hadar
a0ab6931d1 add: match isocrypto api 2019-09-15 01:14:01 +03:00
Marcus Bernales
cf642b75d1 Brought back new changes 2019-09-14 13:20:21 -07:00
Marcus Bernales
7d7b11f9b8 Reset unnecessary prettier changes 2019-09-14 13:19:27 -07:00
Marcus Bernales
7b583f3b41 Remove unnecessary console.log 2019-09-14 13:13:40 -07:00
Marcus Bernales
0b03702152 Update package-lock for travis 2019-09-14 13:08:40 -07:00
Marcus Bernales
71e0be33f7 Switched to webcrypto 2019-09-14 12:56:48 -07:00
Hadar
2027012379 add: update isomorphic-webcrypto 2019-09-14 22:33:19 +03:00
Hadar
502dd9a3db fix: missing taglength for msrcrypto 2019-09-11 16:36:47 +03:00
Hadar
0b31971678 WIP: aeskey from jwk instead of raw for RN 2019-09-11 09:46:08 +03:00
Mark Nadal
2f0d2e3ade version bump for upcoming RAD fixes (currently not all done) 2019-09-10 14:48:10 -07:00
Mark Nadal
ab69258633 adjust format after merge 2019-09-10 14:45:47 -07:00
Mark Nadal
78ae966813 Merge branch 'master' of http://github.com/amark/gun 2019-09-10 14:40:39 -07:00
Mark Nadal
6af5b6c2ac fix SOME of RAD due to GUN queue growing too deep 2019-09-10 14:40:34 -07:00
Hadar
0311969060 WIP: textencoder for RN 2019-09-11 00:21:21 +03:00
Hadar
a2a7ef91bb WIP: buffer for RN 2019-09-11 00:10:59 +03:00
Hadar
5d352d90c9 WIP: expo friendly exports 2019-09-10 23:47:19 +03:00
Hadar
209909ce94 WIP: use isomorphic-webcrypto for RN 2019-09-10 23:30:00 +03:00
Mark Nadal
8a44eb7f62
Merge pull request #810 from GoodDollar/SEA-concurrent-possible-fix
SEA concurrent possible fix
2019-09-06 01:29:10 -07:00
Hadar
0725190a94 Merge remote-tracking branch 'origin/master' into SEA-concurrent-possible-fix 2019-09-05 14:54:13 +03:00
sirpy
fc4a23e148
Merge pull request #1 from amark/master
update to latest gun
2019-09-05 14:53:31 +03:00
Hadar
9fc0adbbe6 add: reflect change in sea.js 2019-09-05 14:40:24 +03:00
Mark Nadal
0a4af74ea9
Merge pull request #809 from Dletta/promise
added promise wrappers to core API
2019-09-04 09:35:12 -07:00
Dletta
d299a99a57 added promise wrappers to core API 2019-09-03 21:37:02 -07:00
Mark Nadal
a34e90e868 for testing @rogowski 's awesome work 2019-08-29 14:43:47 -07:00
Mark Nadal
aa558f0706 don't crash if multicast offline 2019-08-29 14:29:08 -07:00
Mark Nadal
0515403716 use @Artoria2e5 's gold 2019-08-29 14:28:43 -07:00
Mark Nadal
1101c06130 this screwed up RAD folders, undo 2019-08-29 14:28:08 -07:00
Mark Nadal
d148f46d30
Merge pull request #803 from Dletta/then
proposed lib/then
2019-08-29 13:43:58 -07:00
Mark Nadal
a52f9ef3f4
Merge branch 'master' into then 2019-08-29 13:43:47 -07:00
sirpy
597bb77a98
fix: Object.assign modiifies S.ecdh which should be static 2019-08-29 10:54:02 +03:00
Dletta
e0c93228ee proposed lib/then 2019-08-26 21:00:22 -07:00
sirpy
203a80a430
revert argv2 2019-08-20 15:26:11 +03:00
Mark Nadal
9ac94db8b3
Merge pull request #798 from shamsartem/feature-css-fix
Feature css fix
2019-08-19 10:43:28 -07:00
Artsiom Shamsutdzinau
b1aab8cd0f Fix css for chat and todo apps 2019-08-19 13:48:24 +03:00
Mark Nadal
44f5e22bdc
Merge pull request #793 from Tedko/patch-1
Seems like gun-show no longer exist anymore?
2019-08-15 01:27:44 -07:00
Suji Yan
a7083105b6
Seems like gun-show no longer exist anymore?
Trying to find a community solution for GunDB Viewer, seems like gun-show no longer exist. Any idea of any alternative one?

Also @amark  pls double check the status of gun-show. Thanks.
2019-08-14 13:04:12 +08:00
Mark Nadal
37d6d47462
Merge pull request #785 from rogowski/master
RADISK - better performance.
2019-08-05 14:44:44 -07:00
Adriano Rogowski
b4d5f3becc RADISK - better performance. 2019-08-03 01:39:27 -03:00
Mark Nadal
3a7975a235 array options shouldn't be ignored 2019-07-29 15:10:55 -07:00
Mark Nadal
9839f65a69 Merge branch 'master' of http://github.com/amark/gun 2019-07-29 14:43:30 -07:00
Mark Nadal
8dee34d6dc fix #769 for @TensorTom @go1dfish 2019-07-29 14:43:09 -07:00
Mark Nadal
d729ab940f
Merge pull request #782 from skzap/patch-1
Update README.md
2019-07-28 08:14:39 -07:00
heimindanger
f3c925a857
Update README.md
```
npm ERR! code E404
npm ERR! 404 Not Found: @trust/crypto@latest
```

npm module @trust/crypto does not exist anymore
2019-07-28 15:40:03 +02:00
Mark Nadal
cc276a6862 uuid fix (?) for @go1dfish 2019-07-27 19:43:19 -07:00
Mark Nadal
e1ae0158a4 mix node 2019-07-26 15:11:40 -07:00
Mark Nadal
e5589d435c Merge branch 'master' of http://github.com/amark/gun 2019-07-24 15:03:56 -07:00
Mark Nadal
c4864c214b afore! test? 2019-07-24 15:03:54 -07:00
Mark Nadal
8431dce0b9
Merge pull request #772 from negue/fix/lib/open
fix null doc
2019-07-17 11:05:51 -07:00
negue
959e8e36bb
fix null doc 2019-07-17 18:51:47 +02:00
Mark Nadal
68ba4dcc65 need to fix opt more, but hopefully quick fix on axe reconfig that @SunriseFox pointed out 2019-07-17 05:57:31 -07:00
Mark Nadal
3277ba4127 unbuild fixes/updates 2019-07-15 17:03:14 -07:00
Mark Nadal
ced1e20ecc improve .once, but need to review it more/again later 2019-07-15 16:56:06 -07:00
Mark Nadal
9a6495dc21 fix webrtc 2019-07-14 23:38:57 -07:00
Mark Nadal
eb313d6f65 don't make another websocket server when options/config live update 2019-07-14 02:09:25 -07:00
Mark Nadal
ab36283b2e GUN: -peer, once fix, +multicast; AXE +peer +email 2019-07-12 12:17:08 -07:00
Mark Nadal
0c718c8658 hot as a meta doll 2019-07-04 16:40:22 -07:00
Mark Nadal
e52496ae76 allow for COR(B) stats 2019-06-28 13:23:38 -07:00
Mark Nadal
7ce2dc9056 tweak? 2019-06-28 12:45:18 -07:00
Mark Nadal
b2db5f6b43
Merge pull request #761 from amark/debug
axe -> debug -> master -> npm
2019-06-14 04:38:07 -07:00
Mark Nadal
2741996002 bump ossl & ws 2019-06-14 04:32:54 -07:00
Mark Nadal
5b38d06bcb bump ossl 2019-06-14 04:27:02 -07:00
Mark
6033abbdd9 reduce ? 2019-06-14 03:22:12 -07:00
Mark
7bec7ce5a0 go back to routing put 2019-06-13 15:15:59 -07:00
Mark
162061259a test no put route for peer = out count (?) 2019-06-13 15:04:46 -07:00
Mark
6eaa0f5fa6 rad stats 2019-06-13 12:55:47 -07:00
Mark
48f9d10e2a un-indent, woah git diff :/ 2019-06-12 15:01:56 -07:00
Mark
dc42f63204 axe put routes 2019-06-12 01:18:54 -07:00
Mark Nadal
86043aa89e heartbeat is needed? 2019-05-28 02:02:42 -07:00
Mark Nadal
c4ba41c4b8 try axe 2019-05-28 00:36:28 -07:00
Mark Nadal
866593343d
Merge pull request #756 from amark/master
master into debug
2019-05-28 00:31:09 -07:00
Mark Nadal
9f7c530922
Merge branch 'debug' into master 2019-05-28 00:24:31 -07:00
Mark Nadal
cfd7d1042d "fix" holy grail, unbuild. 2019-05-28 00:14:23 -07:00
Mark Nadal
4aac986e94 distribute & cap load 2019-05-27 22:06:38 -07:00
Mark Nadal
209bdf2b06 refactor DAM & add PANIC tests 2019-05-26 04:33:00 -07:00
Mark Nadal
a786944ed6 finally fix pid , I'm sure @rogowski will be delighted! 2019-05-21 00:21:02 -07:00
Mark Nadal
a6a5aec76f have AXE use DAM. 2019-05-20 20:46:55 -07:00
Mark Nadal
5b1a2d1b28 peer duration time 2019-05-20 04:40:26 -07:00
Mark Nadal
c9b0a05ee7 force stop test 2019-05-16 16:25:33 -07:00
Mark Nadal
2945c42ba2 Merge remote-tracking branch 'origin/debug' into debug 2019-05-16 16:23:27 -07:00
Mark Nadal
f82cb32a54
Merge pull request #750 from amark/master
master into debug
2019-05-16 16:22:59 -07:00
Mark Nadal
455e9f9c91 more stats to debug perf 2019-05-16 03:14:26 -07:00
Mark Nadal
ed30911ba7 fix peer leak + more stats 2019-05-15 10:27:57 -07:00
Mark Nadal
9b820287d6 :( make sure test wasn't false not false positive :( for @go1dfish 2019-05-14 11:01:27 -07:00
Mark Nadal
abac11d959 version bump, maybe peer leak fix, @go1dfish feature ++ 2019-05-14 00:51:06 -07:00
Mark Nadal
124d0146c8 fix possible peer leak & add feature for @go1dfish 2019-05-14 00:45:04 -07:00
Mark Nadal
083c1c59f8 visualize memory 2019-05-10 12:47:04 -07:00
Mark Nadal
2b899aeb93
Merge pull request #738 from eugenioclrc/master
Missing condition in rs3
2019-05-07 19:58:45 -07:00
Mark Nadal
3bee20dd8a
Merge branch 'master' into master 2019-05-07 19:57:40 -07:00
Mark Nadal
86e78f1e5d
Merge pull request #746 from sirpy/patch-1
fix: settings can be from opts or from process.env
2019-05-07 19:56:25 -07:00
sirpy
ed9b234c21
fix: settings can be from opts or from process.env 2019-05-06 23:46:35 +03:00
Mark Nadal
1c44bf900e
Merge pull request #744 from nuragic/patch-1
Update nts.html
2019-05-06 11:33:11 -07:00
Andrea Puddu
4c8eb2c112
Update nts.html
Change font family to monospace to prevent the text moving
2019-05-03 12:21:27 +02:00
Mark Nadal
a111e20f27 fix Lex lookups 2019-04-27 15:06:00 -07:00
Mark Nadal
b78e73a32b stats! version bump 2019-04-27 11:55:08 -07:00
Mark Nadal
bf40e7e152 tomap + stats 2019-04-27 11:54:25 -07:00
Mark Nadal
2071512b20 add health stats 2019-04-27 00:16:12 -07:00
Mark Nadal
e9fc38ddd1 disable multicast by default :( 2019-04-26 16:10:56 -07:00
Mark Nadal
16634cf023 bump version 2019-04-26 16:03:57 -07:00
Mark Nadal
2d541f9529 fix queue issue 2019-04-26 16:03:33 -07:00
Mark Nadal
da431c06bc fix tests to not delete folder while running 2019-04-25 20:10:08 -07:00
Mark Nadal
ecffdce786 same process instances should share RAD plugins for same file options to prevent corruption 2019-04-25 20:10:08 -07:00
Mark Nadal
782f66cd49
Merge pull request #741 from go1dfish/patch-4
Fix for gc_info_enable
2019-04-25 13:51:45 -07:00
go1dfish
33893e40b8
Fix for gc_info_enable 2019-04-25 13:48:50 -07:00
Mark Nadal
2c8abba79c
call next in put 2019-04-25 13:22:23 -07:00
Mark Nadal
32a68c2d4d fix Reverse RAD! 2019-04-23 13:28:05 -07:00
Mark Nadal
1abcfe0c83 radix empty end should be infinite 2019-04-22 18:14:02 -07:00
Mark Nadal
4b247628e7 fix / merge. REVERSE RAD! 2019-04-22 17:46:26 -07:00
Mark Nadal
4085a4053c Reverse RAD! 2019-04-22 17:44:38 -07:00
Mark Nadal
98a1290cbc
Merge pull request #739 from toyoko-we/fix
bye.js fix (support new mesh)
2019-04-22 14:13:42 -07:00
Yusuke Sano
f7d9089ba7 bye.js fix (support new mesh)
I am using gundb 0.8.1 0.2019.331 and noticed not working bye method when programming web application on this version.
Maybe this is happened because bye() access to outdated mesh structure.
2019-04-21 17:45:39 +09:00
Mark Nadal
e7e9c35d69
Merge pull request #737 from rogowski/master
AXE - change necessary to work webrtc.
2019-04-20 18:39:23 -07:00
eugenioclrc
29519b0ebd missing condition in rs3
The condition if(!process.env.AWS_S3_BUCKET){ return } force you tu have
env variables, if you config s3 via Gun constructor you could never use
s3
2019-04-20 22:05:23 -03:00
Adriano Rogowski
b99202f0ec AXE - change necessary to work webrtc. 2019-04-20 21:28:04 -03:00
Mark Nadal
9cb1a52fc0 RAD lexical cursors! 2019-04-19 16:57:33 -07:00
Mark Nadal
0bdaf053f0 initial RAD > < but need to decide default behavior 2019-04-19 00:29:38 -07:00
Mark Nadal
d8ce093e9f merge updates with @JK0N 's 2019-04-16 18:27:16 -07:00
Mark Nadal
8bc815e827
Merge pull request #527 from JK0N/master
Fix serve.js module, prevent access to files in parent directories.
2019-04-16 18:24:49 -07:00
Mark Nadal
2e41cef16b
Merge branch 'master' into master 2019-04-16 18:24:42 -07:00
Mark Nadal
7ae2f3d1ec
Merge pull request #733 from rogowski/master
Small change, but necessary.
2019-04-16 18:08:06 -07:00
Mark Nadal
8a36d0863a
Merge branch 'master' into master 2019-04-16 18:05:20 -07:00
Mark Nadal
d947059d5c MULTICAST!!! RAD * update, start start 2019-04-16 17:46:49 -07:00
Mark Nadal
1e20fd5761
Merge pull request #735 from amark/feature-multicast
Multicast transport
2019-04-16 16:45:42 -07:00
Martti Malmi
d11abeb781 gun multicast transport 2019-04-16 16:17:25 -07:00
Martti Malmi
fde74e1254 Merge branch 'master' into feature-multicast 2019-04-15 12:55:41 -07:00
Adriano Rogowski
d3f0d326b2 Testing passing.
`npm run testaxe`

Dont repeat rtc.nnounce.
2019-04-10 21:15:27 -03:00
Adriano Rogowski
ed509d0ae3 Merge branch 'master' of https://github.com/amark/gun 2019-04-08 19:16:53 -03:00
Adriano Rogowski
f7a5937ed5 AXE - chancher lib/super.js to use Rad instead of json plain object. 2019-03-10 17:31:13 -03:00
Adriano Rogowski
f92da9b14d AXE - merge with amark's gun master. 2019-03-10 17:29:45 -03:00
Adriano Rogowski
16a34c1d0c Change mesh to _. 2019-03-10 16:40:56 -03:00
Adriano Rogowski
b4f760f1b5 Pull with amark master 2019-03-10 16:40:06 -03:00
Martti Malmi
be4574840a add /gun to peer url 2019-02-12 13:49:24 +02:00
Martti Malmi
6795ef04a8 better env detection 2019-02-12 11:20:54 +02:00
Martti Malmi
5f153a4912 lib/multicast.js module for finding local peers 2019-02-12 11:20:54 +02:00
Adriano Rogowski
7bc146bcce AXE - new test and example for webrtc. 2019-01-22 22:55:36 -02:00
Adriano Rogowski
803c5b6ee0 AXE - changes for radix DHT. 2019-01-22 22:54:22 -02:00
Adriano Rogowski
259857453a GUN - changes to AXE with radix. 2019-01-22 22:53:49 -02:00
Adriano Rogowski
7bb3aa250e AXE - First PANIC test. 2019-01-10 20:44:20 -02:00
Adriano Rogowski
1869f09a4c Turning on AXE. 2019-01-10 20:38:10 -02:00
Adriano Rogowski
25db64527c Only remove log in axe. 2019-01-10 20:18:03 -02:00
Adriano Rogowski
a6f12af8da Revert changes in gun to solve peer.id undefined. Don't need this change now because we are not using the peer.id in the subscriptions. Maybe will add this in the future. 2019-01-10 20:16:35 -02:00
Adriano Rogowski
dd3d4c3066 Gun Mesh - remove queue from mesh.hi. 2019-01-01 18:30:52 -02:00
Adriano Rogowski
fe7576ee66 GUN - changes to fix peer.id undefined. @amark please validate this change. 2019-01-01 10:40:11 -02:00
Adriano Rogowski
6d12b3d5a9 Gun - mesh. Putting the dam message first solve the problem of the first gun 'in' event come without peer.id. 2018-12-20 21:10:02 -02:00
Adriano Rogowski
51b98ba370 Merge https://github.com/amark/gun 2018-12-20 09:09:26 -02:00
Adriano Rogowski
83f2aec1e3 Log for when peer.id is undefined. 2018-12-02 17:18:42 -02:00
Adriano Rogowski
6fd0e88dd5 Merge https://github.com/amark/gun 2018-11-30 22:43:00 -02:00
Adriano Rogowski
e2a7dba29f AXE - using a garbage collector to remove subscribes from superpeer and resubscribe from peers (browser). 2018-11-30 21:50:40 -02:00
Adriano Rogowski
1e8bece479 Solve: require Gun in Mesh. 2018-11-28 07:31:38 -02:00
Mark Nadal
fc4f3c5157 Merge branch 'master' into debug 2018-05-23 23:18:33 -07:00
Jouni
960031ca4f Fix serve.js module, prevent access to files in parent directories. It's a bad idea to have your code serve your source code but if it needs to be done, this will prevent the most obvious leaks (curl -v --path-as-is 'http://localhost:8080/gun/../../.env') 2018-04-10 18:56:41 +03:00
391 changed files with 46341 additions and 34074 deletions

View File

@ -1,4 +1,6 @@
node_modules
radata
stats.radata
.git
.gitignore
*.md

12
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: amark
patreon: gunDB
open_collective: gun
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

108
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,108 @@
name: ci
on:
push:
pull_request:
jobs:
test:
strategy:
matrix:
node-version: [14.x]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
# checkout the code
- name: Checkout
uses: actions/checkout@v4 # Updated to v4 (latest as of 2025)
# verify the version in package.json matches the release tag
- name: Version
uses: tcurdt/action-verify-version-npm@master # No version update as it's using @master
# setup the node version
- name: Setup Node ${{ matrix.node-version }}
uses: actions/setup-node@v4 # Updated to v4 (latest as of 2025)
with:
node-version: ${{ matrix.node-version }}
# cache node_modules if we can
- name: Cache
id: cache-modules
uses: actions/cache@v4 # Updated to v4 (latest as of 2025)
with:
path: node_modules
key: ${{ matrix.node-version }}-${{ runner.os }}-build-${{ hashFiles('package.json') }}
# otherwise run install
- name: Install
if: steps.cache-modules.outputs.cache-hit != 'true'
run: npm install
# run tests
- name: Test
run: npm test
# create github release
release:
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
needs: [test]
runs-on: ubuntu-latest
steps:
# create github release (which triggers the release workflows)
- name: Release
uses: softprops/action-gh-release@v2 # Updated to v2 (latest stable version as of 2025)
# env:
# GITHUB_TOKEN: ${{ secrets.PAT }}
# # publish latest master or release to dockerhub
# dockerhub:
# if: github.event_name == 'push'
# needs: [test]
# runs-on: ubuntu-latest
# env:
# image: ${{ secrets.DOCKERHUB_USERNAME }}/gun
# steps:
#
# - name: Checkout
# uses: actions/checkout@v4 # Updated to v4
#
# - name: Login
# env:
# DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
# DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
# run: echo -n ${{ secrets.DOCKERHUB_TOKEN }} | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin
#
# - name: Build
# run: |
# echo "SHA=$GITHUB_SHA"
# docker build --build-arg SHA=$GITHUB_SHA \
# BUILD_DATE=$(date +'%Y-%m-%dT%H:%M:%S') \
# VCS_REF=${GITHUB_REF} \
# VCS_URL=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} \
# VERSION=${GITHUB_REF##*/} \
# SHA=$GITHUB_SHA \
# --label "SHA=$GITHUB_SHA" \
# --tag ${{ env.image }}:${GITHUB_REF##*/} \
# --tag ${{ env.image }}:latest \
# .
#
# - name: Push
# run: docker push ${{ env.image }}
# publish release to npm
npm:
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
needs: [test]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4 # Updated to v4
- name: Publish
env:
NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
run: |
npm config set //registry.npmjs.org/:_authToken=$NPM_AUTH_TOKEN
npm install
npm publish --access=public

8
.gitignore vendored
View File

@ -7,7 +7,15 @@ yarn.lock
.idea/
*.bak
*.new
*.log
v8.json
*.DS_store
isolate*.log
.esm-cache
.sessionStorage
.localStorage
/types/**/*.ts
!/types/**/*.d.ts
!/types/**/*.test-d.ts
/gun.ts
/temp/

5
.npmignore Normal file
View File

@ -0,0 +1,5 @@
*.ts
/temp/
!*.d.ts
*.radata
isolate-*

1
.prettierignore Normal file
View File

@ -0,0 +1 @@
*

View File

@ -2,9 +2,9 @@ language: node_js
branches:
except:
- debug
- manhattan
node_js:
- 8
- 10
cache:
directories:
- node_modules
- node_modules

View File

@ -1,20 +1,26 @@
# CHANGELOG
## 0.2020.x
`>0.2020.520` may break in-process `gun1` `gun2` message passing. Check `test/common.js` "Check multi instance message passing" for a hint and/or complain on community chat.
- No breaking changes to core API.
- Storage adapter `put` event breaking change (temporary?), RAD is official now and storage adapters should be RAD plugins instead of GUN adapters.
- GUN soul format changed from being a random UUID to being a more predictable graph path (of where initially created) to support even better offline behavior. This means `null`ing & replacing an object will not create a new but re-merge.
- Pretty much all internal GUN utility will be deleted, these are mostly undocumented but will affect some people - they will still be available as a separate file but deprecated.
- As the DHT gets implemented, your relay peers may automatically connect to it, so do not assume your peer is standalone. `Gun({axe: false` should help prevent this but loses you most scaling properties.
- The 2019 -> 2020 "changes" are happening gradually, based on experimental in-production tests.
- As always, **most important** is to ask in the [community chat](http://chat.gun.eco) if you have any issues, and to keep up to date with changes.
## 0.2019.x
Some RAD & SEA data format changes, but with as much backward compatibility as possible, tho ideally should be dropped.
## 0.9.x
No breaking changes, but the new Radix Storage Engine (RSE) has been finally integrated and works with S3 as a backup. Expect an order of magnitude or more in cost savings, we'll report our December bill compared to November when we get it.
No breaking changes, but the new Radix Storage Engine (RSE) has been finally integrated and works with S3 as a backup.
We have successfully benchmarked it against **1,000,000 records** doing end-to-end triple verification using our Jepsen-inspired [PANIC](https://github.com/gundb/panic-server) distritubed testing framework, doing **~4K acked writes/second** on a Macbook Air dev machine. For more information on PANIC, check out this [5 minute presentation](https://youtu.be/nTbUCTgLmkY) we did in Sweden (and the prior talk, for those interested in [porting GUN](https://github.com/amark/gun/wiki/porting-gun) out of its reference implementation of JS).
> Warning: There is a known rare edge case in RSE currently, if data is split between two chunked files, a GET will only return from the first chunk. This will be fixed soon, but we still encourage developers to run and test against it, please report any problems.
To use RSE, initialize a gun server peer with the default storage disabled, like `Gun({localStorage: false})`. Want to use it with S3? All you need to do is make sure that your environment variables are configured and it will automatically use S3, here is a [template](https://github.com/amark/gun/wiki/Using-Amazon-S3-for-Storage). This works especially well for our 1-click-deploy Heroku [demo server](http://gunjs.herokuapp.com/) with the example apps.
Finally, with **end-to-end encryption** being enabled with our Security, Encryption, Authorization (SEA) framework (check out our [P2P/decentralized crypto-identity blockchain](https://github.com/amark/gun/wiki/auth)), gun is marching towards a stable v1.0 production-ready system (it is already being used in production by a Northern European government's Navy). So if you are able to [work around the remaining bugs](https://github.com/amark/gun/issues), we would appreciate everybody efforts in experimenting and testing out gun and reporting any last hiccups **in our lead up to the v1.0**!
We will be **overhauling documentation in this v0.9.x series**, please make complaints about what is missing, and how we can make it better, so it will be polished for the v1.0! The [chatroom is actively and friendly for help](https://gitter.im/amark/gun), [StackOverflow](https://stackoverflow.com/questions/tagged/gun) for questions. And we're looking for [sponsors](https://www.patreon.com/gunDB), we **regularly get 1,200+ uniques every 2 weeks** on this repo, we've had **53% monthly growth** on our installs, and GUN is ranked in the top quarter of the top 1% of the top 1% fastest growing projects across all GitHub! If you are an Enterprise, this would be a great time to chat with us about our IoT, AI/ML, edge computing, graph, and cybersecurity solutions.
Here is towards a v1.0! Cheers.
// Edit: commentary removed.
## 0.8.x

View File

@ -1,5 +1,15 @@
FROM alpine:latest
# Build-time metadata as defined at http://label-schema.org
# install packages
FROM node:lts-alpine as builder
RUN mkdir /work
WORKDIR /work
RUN apk add --no-cache alpine-sdk python3
COPY package*.json ./
RUN mkdir -p node_modules
RUN npm ci --only=production
# fresh image without dev packages
FROM node:lts-alpine
# build-time metadata as defined at http://label-schema.org
ARG BUILD_DATE
ARG VCS_REF
ARG VCS_URL
@ -12,15 +22,14 @@ LABEL org.label-schema.build-date=$BUILD_DATE \
org.label-schema.vendor="The Gun Database Team" \
org.label-schema.version=$VERSION \
org.label-schema.schema-version="1.0"
# org.label-schema.description="Let it be pulled from Readme.md..." \
WORKDIR /app
ARG SHA
RUN mkdir /work
WORKDIR /work
COPY --from=builder /work/node_modules ./node_modules
RUN npm rebuild -q
ADD . .
ENV NPM_CONFIG_LOGLEVEL warn
RUN apk update && apk upgrade \
&& apk add --no-cache ca-certificates nodejs-npm \
&& apk add --no-cache --virtual .build-dependencies python make g++ \
&& npm install \
&& apk del .build-dependencies && rm -rf /var/cache/* /tmp/npm*
RUN echo "{ \"sha\": \"$SHA\" }" > version.json
RUN cat version.json
EXPOSE 8080
EXPOSE 8765
CMD ["npm","start"]

View File

@ -1 +1 @@
web: node --optimize_for_size --gc_interval=100 examples/http.js
web: node --inspect examples/http.js

207
README.md
View File

@ -1,64 +1,73 @@
<p><a href="https://gun.eco/"><img width="40%" src="https://cldup.com/TEy9yGh45l.svg"/></a><img width="50%" align="right" vspace="25" src="https://gun.eco/see/demo.gif"/></p>
<p id="readme"><a href="https://gun.eco/"><img width="40%" src="https://cldup.com/TEy9yGh45l.svg"/></a><img width="50%" align="right" vspace="25" src="https://gun.eco/see/demo.gif"/></p>
[![npm](https://img.shields.io/npm/dm/gun.svg)](https://www.npmjs.com/package/gun)
[![Travis](https://img.shields.io/travis/amark/gun/master.svg)](https://travis-ci.org/amark/gun)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Famark%2Fgun.svg?size=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Famark%2Fgun?ref=badge_shield)
[![Gitter](https://img.shields.io/gitter/room/amark/gun.js.svg)](https://gitter.im/amark/gun)
[![](https://data.jsdelivr.com/v1/package/npm/gun/badge?style=rounded)](https://www.jsdelivr.com/package/npm/gun)
[![](https://data.jsdelivr.com/v1/package/npm/gun/badge)](https://www.jsdelivr.com/package/npm/gun)
![Build](https://github.com/amark/gun/actions/workflows/ci.yml/badge.svg)
[![Gitter](https://img.shields.io/gitter/room/amark/gun.js.svg)](http://chat.gun.eco)
**GUN** is an _ecosystem_ of tools that let you <u>build tomorrow's dApps, today</u>.
**GUN** is an [ecosystem](https://gun.eco/docs/Ecosystem) of **tools** that let you build [community run](https://www.nbcnews.com/tech/tech-news/these-technologists-think-internet-broken-so-they-re-building-another-n1030136) and [encrypted applications](https://gun.eco/docs/Cartoon-Cryptography) - like an Open Source Firebase or a Decentralized Dropbox.
Decentralized alternatives to [Reddit](https://notabug.io/t/whatever/comments/36588a16b9008da4e3f15663c2225e949eca4a15/gpu-bot-test), [YouTube](https://d.tube/), [Wikipedia](https://news.ycombinator.com/item?id=17685682), etc. are already pushing terabytes of daily P2P traffic on GUN. We are a [friendly community](https://gitter.im/amark/gun) creating a free fun future for freedom:
The [Internet Archive](https://news.ycombinator.com/item?id=17685682) and [100s of other apps](https://github.com/amark/gun/wiki/awesome-gun) run GUN in-production.
+ Multiplayer by default with realtime p2p state synchronization!
+ Graph data lets you use key/value, tables, documents, videos, & more!
+ Local-first, offline, and decentralized with end-to-end encryption.
Decentralized alternatives to [Zoom](https://www.zdnet.com/article/era-hatches-meething-an-open-source-browser-based-video-conferencing-system/), [Reddit](https://notabug.io/t/whatever/comments/36588a16b9008da4e3f15663c2225e949eca4a15/gpu-bot-test), [Instagram](https://iris.to/), [Slack](https://iris.to/), [YouTube](https://d.tube/), [Stripe](https://twitter.com/marknadal/status/1422717427427647489), [Wikipedia](https://news.ycombinator.com/item?id=17685682), Facebook [Horizon](https://twitter.com/marknadal/status/1424476179189305347) and more have already pushed terabytes of daily P2P traffic on GUN. We are a [friendly community](http://chat.gun.eco/) creating a [free fun future for freedom](https://youtu.be/1HJdrBk3BlE):
<table>
<tr>
<a href=""><img width="31%" align="left" src="https://gun.eco/see/3dvr.gif" title="3D VR"/></a>
<a href="https://github.com/cstefanache/cstefanache.github.io/blob/master/_posts/2016-08-02-gun-db-artificial-knowledge-sharing.md#gundb"><img width="31%" align="left" src="https://gun.eco/see/aiml.gif" title="AI/ML"/></a>
<a href="http://gps.gunDB.io/"><img width="31%" align="left" src="https://gun.eco/see/gps.gif" title="GPS"/></a>
<a href="https://github.com/lmangani/gun-scape#gun-scape"><img width="31%" align="left" src="https://gun.eco/see/dataviz.gif" title="Data Viz"/></a>
<a href="https://github.com/amark/gun/wiki/Auth"><img width="31%" align="left" src="https://gun.eco/see/p2p.gif" title="P2P"/></a>
<a href="https://github.com/Stefdv/gun-ui-lcd#okay-what-about-gundb-"><img width="31%" align="left" src="https://gun.eco/see/iot.gif" title="IoT"/></a>
<a href="https://youtu.be/s_m16-w6bBI"><img width="31%" src="https://gun.eco/see/3dvr.gif" title="3D VR"/></a>
<a href="https://github.com/cstefanache/cstefanache.github.io/blob/06697003449e4fc531fd32ee068bab532976f47b/_posts/2016-08-02-gun-db-artificial-knowledge-sharing.md"><img width="31%" src="https://gun.eco/see/aiml.gif" title="AI/ML"/></a>
<a href="http://gps.gunDB.io/"><img width="31%" src="https://gun.eco/see/gps.gif" title="GPS"/></a>
</tr>
<tr>
<a href="https://github.com/lmangani/gun-scape#gun-scape"><img width="31%" src="https://gun.eco/see/dataviz.gif" title="Data Viz"/></a>
<a href="https://github.com/amark/gun/wiki/Auth"><img width="31%" src="https://gun.eco/see/p2p.gif" title="P2P"/></a>
<a href="https://github.com/Stefdv/gun-ui-lcd#okay-what-about-gundb-"><img width="31%" src="https://gun.eco/see/iot.gif" title="IoT"/></a>
</tr>
<tr>
<a href="http://chat.gun.eco"><img width="31%" src="https://gun.eco/see/vr-world.gif" title="VR World"/></a>
<a href="https://youtu.be/1ASrmQ-CwX4"><img width="31%" src="https://gun.eco/see/ar.gif" title="AR"/></a>
<a href="https://meething.space/"><img width="31%" src="https://gun.eco/see/video-conf.gif" title="Video Confernece"/></a>
</tr>
</table>
The ecosystem is one nice stack of technologies that looks like this:
<div><img width="48%" src="https://gun.eco/see/stack.png"/>
<img width="48%" align="right" src="https://gun.eco/see/layers.png"/></div>
For now, it is best to start with GUN and _just use it_ to learn the basics, since it is _**so easy**_: (**or** want to read more? Skip ahead to the "[What is GUN?](#what-is-gun)" section.)
## Quickstart
GUN is *super easy* to get started with:
- Try the [interactive tutorial](https://gun.eco/docs/Todo-Dapp) in the browser (**5min** ~ average developer).
- Or `npm install gun` and run the examples with `cd node_modules/gun && npm start` (**5min** ~ average developer).
> **Note:** If you don't have [node](http://nodejs.org/) or [npm](https://www.npmjs.com/), read [this](https://github.com/amark/gun/blob/master/examples/install.sh) first.
> If the `npm` command line didn't work, you may need to `mkdir node_modules` first or use `sudo`.
- An online demo of the examples are available here: http://gunjs.herokuapp.com/
- Or write a quick app: ([try now in jsbin](http://jsbin.com/sovihaveso/edit?js,console))
- An online demo of the examples are available here: http://try.axe.eco/
- Or write a quick app: ([try now in a playground](https://jsbin.com/kadobamevo/edit?js,console))
```html
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
<script>
// var Gun = require('gun'); // in NodeJS
// var Gun = require('gun/gun'); // in React
var gun = Gun();
// import GUN from 'gun'; // in ESM
// GUN = require('gun'); // in NodeJS
// GUN = require('gun/gun'); // in React
gun = GUN();
gun.get('mark').put({
name: "Mark",
email: "mark@gunDB.io",
email: "mark@gun.eco",
});
gun.get('mark').on(function(data, key){
console.log("update:", data);
gun.get('mark').on((data, key) => {
console.log("realtime updates:", data);
});
setInterval(() => { gun.get('mark').get('live').put(Math.random()) }, 9);
</script>
```
- Or try something **mind blowing**, like saving circular references to a table of documents! ([play](http://jsbin.com/wefozepume/edit?js,console))
```javascript
var cat = {name: "Fluffy", species: "kitty"};
var mark = {boss: cat};
cat = {name: "Fluffy", species: "kitty"};
mark = {boss: cat};
cat.slave = mark;
// partial updates merge with existing data!
@ -66,13 +75,13 @@ gun.get('mark').put(mark);
// access the data as if it is a document.
gun.get('mark').get('boss').get('name').once(function(data, key){
// `val` grabs the data once, no subscriptions.
// `once` grabs the data once, no subscriptions.
console.log("Mark's boss is", data);
});
// traverse a graph of circular references!
gun.get('mark').get('boss').get('slave').once(function(data, key){
console.log("Mark is the slave!", data);
console.log("Mark is the cat's slave!", data);
});
// add both of them to a table!
@ -85,21 +94,41 @@ gun.get('list').map().once(function(data, key){
});
// live update the table!
gun.get('list').set({type: "cucumber", goal: "scare cat"});
gun.get('list').set({type: "cucumber", goal: "jumping cat"});
```
Want to keep building more? **Jump to [THE DOCUMENTATION](#documentation)!**
# What is GUN?
# About
First & foremost, GUN is **a community of the nicest and most helpful people** out there. So [I want to invite you](http://chat.gun.eco) to come tell us about what **you** are working on & wanting to build (new or old school alike! Just be nice as well.) and ask us your questions directly. :)
First & foremost, GUN is **a community of the nicest and most helpful people** out there. So [I want to invite you](https://gitter.im/amark/gun) to come tell us about what **you** are working on & wanting to build (new or old school alike! Just be nice as well.) and ask us your questions directly. :)
<p align="center"><a href="https://www.youtube.com/watch?v=oTQXzhm8w_8"><img width="250" src="https://img.youtube.com/vi/oTQXzhm8w_8/0.jpg"><br/>Watch the 100 second intro!</a></p>
The GUN ecosystem stack is a collection of independent and modular tools covering everything from [CRDT](https://crdt.tech/) [conflict resolution](https://gun.eco/distributed/matters.html), [cryptographic security](https://gun.eco/docs/Cartoon-Cryptography) & [encryption](https://gun.eco/docs/SEA), [radix storage serialization](https://gun.eco/docs/RAD), [mesh networking](https://gun.eco/docs/DAM) & [routing algorithms](https://gun.eco/docs/Routing), to distributed systems [correctness & load testing](https://github.com/gundb/panic-server), CPU scheduled [JSON parser](https://github.com/amark/gun/blob/master/lib/yson.js) to prevent UI lag, and more!
<div><img width="48%" src="https://gun.eco/see/stack.png"/>
<img width="48%" align="right" src="https://gun.eco/see/layers.png"/></div>
On that note, let's get some official shout outs covered first:
### Support
<p align="center">
Thanks to:<br/>
Thanks to:
<table>
<tr>
<td vlign="center"><a href="https://mozilla.org/builders"><img height="100" src="https://user-images.githubusercontent.com/1423657/81992335-85346480-9643-11ea-8754-8275e98e06bc.png"></a></td>
<td vlign="center"><a href="http://unstoppabledomains.com/"><img src="https://gun.eco/img/unstoppable.png"></a></td>
<td vlign="center"><a href="https://mask.io/"><img src="https://dimensiondev.github.io/Mask-VI/assets/Logo/MB--Logo--CombH-Circle--Blue.svg" width="250"></a></td>
</tr>
<tr>
<td vlign="center">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="https://www.ajar.org/"><img src="https://www.ajar.org/logo.png" height="120"></a></td>
<td vlign="center"><a href="https://wallie.io/"><img src="https://raw.githubusercontent.com/gundb/gun-site/master/img/wallie.png" width="250"></a></td>
<td vlign="center">&nbsp;&nbsp;<a href="https://ghostdrive.com/"><img src="https://gun.eco/img/ghostdrive.png" height="120"></a></td>
</tr>
</table>
<a href="https://github.com/robertheessels">Robert Heessels</a>,
<a href="http://qxip.net/">Lorenzo Mangani</a>,
<a href="https://nlnet.nl/">NLnet Foundation</a>,
@ -113,13 +142,28 @@ Thanks to:<br/>
<a href="http://github.com/alanmimms">Alan Mimms</a>,
<a href="https://github.com/dfreire">Dário Freire</a>,
<a href="http://github.com/velua">John Williamson</a>,
<a href="http://github.com/finwo">Robin Bron</a>
<a href="http://github.com/finwo">Robin Bron</a>,
<a href="http://github.com/ElieMakhoul">Elie Makhoul</a>,
<a href="http://github.com/mikestaub">Mike Staub</a>,
<a href="http://github.com/bmatusiak">Bradley Matusiak</a>,
<a href="https://github.com/sjuxax">Jeff Cook</a>,
<a href="https://github.com/nmauersberg">Nico</a>,
<a href="https://github.com/ajartille">Aaron Artille</a>,
<a href="https://github.com/timjrobinson">Tim Robinson</a>,
<a href="https://github.com/hibas123">Fabian Stamm</a>,
<a href="https://twitter.com/mikestaub">Mike Staub</a>,
<a href="https://hunterowens.com/">Hunter Owens</a>,
<a href="https://github.com/JacobMillner">Jacob Millner</a>,
<a href="https://github.com/b-lack">Gerrit Balindt</a>,
<a href="https://github.com/gabriellemon">Gabriel Lemon</a>,
<a href="https://github.com/murageyun">Murage Martin</a>,
<a href="https://github.com/octalmage">Jason Stallings</a>
</p>
- Join others in sponsoring code: https://www.patreon.com/gunDB !
- Ask questions: http://stackoverflow.com/questions/tagged/gun ?
- Found a bug? Report at: https://github.com/amark/gun/issues ;
- **Need help**? Chat with us: https://gitter.im/amark/gun .
- **Need help**? Chat with us: http://chat.gun.eco .
### History
@ -146,11 +190,11 @@ Technically, **GUN is a graph synchronization protocol** with a *lightweight emb
<tr>
<td style="border: 0;"><h3><a href="https://github.com/brysgo/graphql-gun">GraphQL</a></h3></td>
<td style="border: 0;"><h3><a href="https://github.com/PenguinMan98/electrontest">Electron</a></h3></td>
<td style="border: 0;"><h3><a href="https://gun.eco/docs/React-Native">React Native</a></h3></td>
<td style="border: 0;"><h3><a href="https://gun.eco/docs/React-Native">React & Native</a></h3></td>
</tr>
<tr>
<td style="border: 0;"><h3><a href="https://github.com/sjones6/vue-gun">Vue</a></h3></td>
<td style="border: 0;"><h3><a href="https://github.com/amark/gun/wiki/React-Tutorial">React</a></h3></td>
<td style="border: 0;"><h3><a href="https://gun.eco/docs/Svelte">Svelte</a></h3></td>
<td style="border: 0;"><h3><a href="https://github.com/Stefdv/gun-ui-lcd#syncing">Webcomponents</a></h3></td>
</tr>
<tr>
@ -160,54 +204,74 @@ Technically, **GUN is a graph synchronization protocol** with a *lightweight emb
</tr>
<tr>
<td style="border: 0;"><h3><a href="https://gun.eco/docs/Auth">Crypto Auth</a></h3></td>
<td style="border: 0;"><h3><a href="https://gun.eco/docs/Awesome-GUN">Modules</a></h3></td>
<td style="border: 0;"><h3><a href="https://github.com/amark/gun/wiki/Awesome-GUN">Modules</a></h3></td>
<td style="border: 0;"><h3><a href="https://gun.eco/docs/Roadmap">Roadmap</a></h3></td>
</tr>
</table>
This would not be possible without **community contributors**, big shout out to:
**[ajmeyghani](https://github.com/ajmeyghani) ([Learn GUN Basics with Diagrams](https://medium.com/@ajmeyghani/gundb-a-graph-database-in-javascript-3860a08d873c))**; **[anywhichway](https://github.com/anywhichway) ([Block Storage](https://github.com/anywhichway/gun-block))**; **[beebase](https://github.com/beebase) ([Quasar](https://github.com/beebase/gun-vuex-quasar))**; **[BrockAtkinson](https://github.com/BrockAtkinson) ([brunch config](https://github.com/BrockAtkinson/brunch-gun))**; **[Brysgo](https://github.com/brysgo) ([GraphQL](https://github.com/brysgo/graphql-gun))**; **[d3x0r](https://github.com/d3x0r) ([SQLite](https://github.com/d3x0r/gun-db))**; **[forrestjt](https://github.com/forrestjt) ([file.js](https://github.com/amark/gun/blob/master/lib/file.js))**; **[hillct](https://github.com/hillct) (Docker)**; **[JosePedroDias](https://github.com/josepedrodias) ([graph visualizer](http://acor.sl.pt:9966))**; **[JuniperChicago](https://github.com/JuniperChicago) ([cycle.js bindings](https://github.com/JuniperChicago/cycle-gun))**; **[jveres](https://github.com/jveres) ([todoMVC](https://github.com/jveres/todomvc))**; **[kristianmandrup](https://github.com/kristianmandrup) ([edge](https://github.com/kristianmandrup/gun-edge))**; **[Lightnet](https://github.com/Lightnet)** ([Awesome Vue User Examples](https://glitch.com/edit/#!/jsvuegunui?path=README.md:1:0) & [User Kitchen Sink Playground](https://gdb-auth-vue-node.glitch.me/)); **[lmangani](https://github.com/lmangani) ([Cytoscape Visualizer](https://github.com/lmangani/gun-scape), [Cassandra](https://github.com/lmangani/gun-cassandra), [Fastify](https://github.com/lmangani/fastify-gundb), [LetsEncrypt](https://github.com/lmangani/polyGun-letsencrypt))**; **[mhelander](https://github.com/mhelander) ([SEA](https://github.com/amark/gun/blob/master/sea.js))**; [omarzion](https://github.com/omarzion) ([Sticky Note App](https://github.com/omarzion/stickies)); [PsychoLlama](https://github.com/PsychoLlama) ([LevelDB](https://github.com/PsychoLlama/gun-level)); **[RangerMauve](https://github.com/RangerMauve) ([schema](https://github.com/gundb/gun-schema))**; **[robertheessels](https://github.com/swifty) ([gun-p2p-auth](https://github.com/swifty/gun-p2p-auth))**; **[rogowski](https://github.com/rogowski) (AXE)**; [sbeleidy](https://github.com/sbeleidy); **[sbiaudet](https://github.com/sbiaudet) ([C# Port](https://github.com/sbiaudet/cs-gun))**; **[Sean Matheson](https://github.com/ctrlplusb) ([Observable/RxJS/Most.js bindings](https://github.com/ctrlplusb/gun-most))**; **[Shadyzpop](https://github.com/Shadyzpop) ([React Native example](https://github.com/amark/gun/tree/master/examples/react-native))**; **[sjones6](https://github.com/sjones6) ([Flint](https://github.com/sjones6/gun-flint))**; **[Stefdv](https://github.com/stefdv) (Polymer/web components)**; **[zrrrzzt](https://github.com/zrrrzzt) ([JWT Auth](https://gist.github.com/zrrrzzt/6f88dc3cedee4ee18588236756d2cfce))**; **[xmonader](https://github.com/xmonader) ([Python Port](https://github.com/xmonader/pygundb))**; **[88dev](https://github.com/88dev) ([Database Viewer](https://github.com/88dev/gun-show))**;
**[ajmeyghani](https://github.com/ajmeyghani) ([Learn GUN Basics with Diagrams](https://medium.com/@ajmeyghani/gundb-a-graph-database-in-javascript-3860a08d873c))**; **[anywhichway](https://github.com/anywhichway) ([Block Storage](https://github.com/anywhichway/gun-block))**; **[beebase](https://github.com/beebase) ([Quasar](https://github.com/beebase/gun-vuex-quasar))**; **[BrockAtkinson](https://github.com/BrockAtkinson) ([brunch config](https://github.com/BrockAtkinson/brunch-gun))**; **[Brysgo](https://github.com/brysgo) ([GraphQL](https://github.com/brysgo/graphql-gun))**; **[d3x0r](https://github.com/d3x0r) ([SQLite](https://github.com/d3x0r/gun-db))**; **[forrestjt](https://github.com/forrestjt) ([file.js](https://github.com/amark/gun/blob/master/lib/file.js))**; **[hillct](https://github.com/hillct) (Docker)**; **[JosePedroDias](https://github.com/josepedrodias) ([graph visualizer](http://acor.sl.pt:9966))**; **[JuniperChicago](https://github.com/JuniperChicago) ([cycle.js bindings](https://github.com/JuniperChicago/cycle-gun))**; **[jveres](https://github.com/jveres) ([todoMVC](https://github.com/jveres/todomvc))**; **[kristianmandrup](https://github.com/kristianmandrup) ([edge](https://github.com/kristianmandrup/gun-edge))**; **[Lightnet](https://github.com/Lightnet)** ([Awesome Vue User Examples](https://glitch.com/edit/#!/jsvuegunui?path=README.md:1:0) & [User Kitchen Sink Playground](https://gdb-auth-vue-node.glitch.me/)); **[lmangani](https://github.com/lmangani) ([Cytoscape Visualizer](https://github.com/lmangani/gun-scape), [Cassandra](https://github.com/lmangani/gun-cassandra), [Fastify](https://github.com/lmangani/fastify-gundb), [LetsEncrypt](https://github.com/lmangani/polyGun-letsencrypt))**; **[mhelander](https://github.com/mhelander) ([SEA](https://github.com/amark/gun/blob/master/sea.js))**; [omarzion](https://github.com/omarzion) ([Sticky Note App](https://github.com/omarzion/stickies)); [PsychoLlama](https://github.com/PsychoLlama) ([LevelDB](https://github.com/PsychoLlama/gun-level)); **[RangerMauve](https://github.com/RangerMauve) ([schema](https://github.com/gundb/gun-schema))**; **[robertheessels](https://github.com/swifty) ([gun-p2p-auth](https://github.com/swifty/gun-p2p-auth))**; **[rogowski](https://github.com/rogowski) (AXE)**; [sbeleidy](https://github.com/sbeleidy); **[sbiaudet](https://github.com/sbiaudet) ([C# Port](https://github.com/sbiaudet/cs-gun))**; **[Sean Matheson](https://github.com/ctrlplusb) ([Observable/RxJS/Most.js bindings](https://github.com/ctrlplusb/gun-most))**; **[Shadyzpop](https://github.com/Shadyzpop) ([React Native example](https://github.com/amark/gun/tree/master/examples/react-native))**; **[sjones6](https://github.com/sjones6) ([Flint](https://github.com/sjones6/gun-flint))**; RIP **[Stefdv](https://github.com/stefdv) (Polymer/web components)**; **[zrrrzzt](https://github.com/zrrrzzt) ([JWT Auth](https://gist.github.com/zrrrzzt/6f88dc3cedee4ee18588236756d2cfce))**; **[xmonader](https://github.com/xmonader) ([Python Port](https://github.com/xmonader/pygundb))**;
I am missing many others, apologies, will be adding them soon!
I am missing many others, apologies, will be adding them soon! This list is infinitely old & way out of date, if you want to be listed in it please make a PR! :)
## Testing
Tests may be run with `npm test`. Tests will trigger persistent writes to the DB, so subsequent runs of the test will fail. You must clear the DB before running the tests again. This can be done by running the following command in the project directory.
You will need to `npm install -g mocha` first. Then in the gun root folder run `npm test`. Tests will trigger persistent writes to the DB, so subsequent runs of the test will fail. You must clear the DB before running the tests again. This can be done by running `rm -rf *data*` command in the project directory.
```bash
rm -rf *data*
```
## Shims
### Additional Cryptography Libraries
> These are only needed for NodeJS & React Native, they shim the native Browser WebCrypto API.
To install with npm, first install `npm install gun -S`.
For just the networking layer, import Gun:
If you want to use [SEA](https://gun.eco/docs/SEA) for `User` auth and security, you will need to install:
`npm install @peculiar/webcrypto --save`
Please see [our React Native docs](https://gun.eco/docs/React-Native) for installation instructions!
Then you can require [SEA](https://gun.eco/docs/SEA) without an error:
```javascript
var Gun = require('gun/gun');
GUN = require('gun/gun');
SEA = require('gun/sea');
```
If you also need to install SEA for user auth and crypto, also install some of its dependencies like this:
`npm install @trust/crypto text-encoding node-webcrypto-ossl --save`
You will need to require it too (it will be automatically added to the Gun object):
```javascript
var Gun = require('gun/gun');
var Sea = require('gun/sea');
```
## Deploy
To quickly spin up a Gun test server for your development team, utilize either [Heroku](http://heroku.com) or [Docker](http://docker.com) or any variant thereof [Dokku](http://dokku.viewdocs.io/dokku/), [Flynn.io](http://flynn.io), [now.sh](https://zeit.co/now), etc. !
> Note: The default examples that get auto-deployed on `npm start` CDN-ify all GUN files, modules, & storage.
> Note: Moving forward, AXE will start to automatically cluster your peer into a shared DHT. You may want to disable this to run an isolated network.
> Note: When deploying a web application using GUN on a cloud provider, you may have to set `CI=false` in your `.env`. This prevents GUN-specific warnings from being treated as errors when deploying your app. You may also resolve this by modifying your webpack config to not try to build the GUN dependencies.
To quickly spin up a GUN relay peer for your development team, utilize [Heroku](http://heroku.com), [Docker](http://docker.com), or any others listed below. Or some variant thereof [Dokku](http://dokku.viewdocs.io/dokku/), K8s, etc. ! Or use all of them so your relays are decentralized too!
### Linux
`SSH` into the home directory of a clean OS install with `sudo` ability. Set any environment variables you need (see below), then do:
```bash
curl -o- https://raw.githubusercontent.com/amark/gun/master/examples/install.sh | bash
```
> Read [install.sh](https://github.com/amark/gun/blob/master/examples/install.sh) first!
> If `curl` is not found, *copy&paste* the contents of install.sh into your ssh.
You can now safely `CTRL+A+D` to escape without stopping the peer. To stop everything `killall screen` or `killall node`.
Environment variables may need to be set like `export HTTPS_CERT=~/cert.pem HTTPS_KEY=~/key.pem PORT=443`. You can also look at a sample [nginx](https://gun.eco/docs/nginx) config. For production deployments, you probably will want to use something like `pm2` or better to keep the peer alive after machine reboots.
### [Dome](https://www.trydome.io/)
[Deploy GUN in one-click](https://app.trydome.io/signup?package=gun) with [Dome](https://trydome.io) and receive a free trial:
[![Deploy to Dome](https://trydome.io/button.svg)](https://app.trydome.io/signup?package=gun)
### [Heroku](https://www.heroku.com/)
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/amark/gun)
> Heroku deletes your data every 15 minutes, one way to fix this is by adding [cheap storage](https://gun.eco/docs/Using-Amazon-S3-for-Storage).
Or:
```bash
@ -217,20 +281,19 @@ heroku create
git push -f heroku HEAD:master
```
Then visit the URL in the output of the 'heroku create' step, in a browser.
Then visit the URL in the output of the 'heroku create' step, in a browser. Make sure to set any environment config vars in the settings tab.
### [Now.sh](https://zeit.co/now/)
### [Zeet.co](https://www.zeet.co/)
```bash
npm install -g now
now --npm amark/gun
```
[![Deploy](https://deploy.zeet.co/gun.svg)](https://deploy.zeet.co/?url=https://github.com/amark/gun)
Then visit the URL in the output of the 'now --npm' step, in your browser.
### [Docker](https://www.docker.com/)
[![Docker Automated buil](https://img.shields.io/docker/automated/gundb/gun.svg)](https://hub.docker.com/r/gundb/gun/) [![](https://images.microbadger.com/badges/image/gundb/gun.svg)](https://microbadger.com/images/gundb/gun "Get your own image badge on microbadger.com") [![Docker Pulls](https://img.shields.io/docker/pulls/gundb/gun.svg)](https://hub.docker.com/r/gundb/gun/) [![Docker Stars](https://img.shields.io/docker/stars/gundb/gun.svg)](https://hub.docker.com/r/gundb/gun/)
> Warning: Docker image is community contributed and may be old with missing security updates, please check version numbers to compare.
[![Docker Automated build](https://img.shields.io/docker/automated/gundb/gun.svg)](https://hub.docker.com/r/gundb/gun/) [![](https://images.microbadger.com/badges/image/gundb/gun.svg)](https://microbadger.com/images/gundb/gun "Get your own image badge on microbadger.com") [![Docker Pulls](https://img.shields.io/docker/pulls/gundb/gun.svg)](https://hub.docker.com/r/gundb/gun/) [![Docker Stars](https://img.shields.io/docker/stars/gundb/gun.svg)](https://hub.docker.com/r/gundb/gun/)
Pull from the [Docker Hub](https://hub.docker.com/r/gundb/gun/) [![](https://images.microbadger.com/badges/commit/gundb/gun.svg)](https://microbadger.com/images/gundb/gun). Or:

25
RELEASE.md Normal file
View File

@ -0,0 +1,25 @@
Every push or pull request will
- run the tests
Every push to master will
- run the tests
- publish the latest docker image to dockerhub
Creating a tag that starts with `v` will
- create a new github release
- publish the release to npm
- publish the release to dockerhub
Creating a release from the github web interface will
- publish the release to npm
- publish the release to dockerhub
Creating the release for version `0.2021.001` from the command line works as follows
git tag v0.2021.001
git push --tags

47
SECURITY.md Normal file
View File

@ -0,0 +1,47 @@
# Security Policy
## Introduction
Security is our top priority. We are committed to ensuring that our project is as secure as possible for everyone who uses it. This document outlines our security policy and procedures for dealing with security issues.
## Supported Versions
We provide security updates for the following versions of our project:
| Version | Supported |
| ------- | ------------------ |
| 0.2020.x| :white_check_mark: |
| < 0.2020| :x: |
## Reporting a Vulnerability
If you discover a vulnerability, we would like to know about it so we can take steps to address it as quickly as possible.
### Report Format
When reporting vulnerabilities, please include the following details:
- Description of the vulnerability
- Steps to reproduce the issue
- Potential impact if left unaddressed
- Suggested mitigation or resolution if any
### Response Time
We aim to confirm the receipt of your vulnerability report within 48 hours. Depending on the severity and complexity of the issue, we strive to investigate the issue and provide an initial response within a week.
### Disclosure Policy
If the vulnerability is confirmed, we will work on a fix and plan a release. We ask that you do not publicly disclose the issue until it has been addressed by us.
## Security Practices
We follow industry-standard security practices, including regular audits of the services and features we provide, to maintain the trust of our users.
## Security Updates
We will communicate any security updates through our standard communication channels, including our project's release notes and official website.
## Conclusion
We greatly value the work of security researchers and believe that responsible disclosure of vulnerabilities is a valuable contribution to the security of the Internet. We encourage users to contribute to the security of our project by reporting any security-related issues to us.

View File

@ -1,8 +1,18 @@
{
"name": "gun-server",
"website": "http://gun.js.org",
"website": "http://gun.eco/",
"repository": "https://github.com/amark/gun",
"logo": "https://avatars3.githubusercontent.com/u/8811914",
"keywords": ["node", "gun", "gunDB", "database","graph","offline-first"],
"description": "Javascript, Offline-First Javascript Graph Database Server Peer"
"description": "Javascript, Offline-First Javascript Graph Database Server Peer",
"env": {
"NPM_CONFIG_PRODUCTION": {
"description": "If you do not want default features, set to \"true\".",
"value": "false"
},
"PEERS": {
"description": "Comma-separated list of peer urls to connect to",
"required": false
}
}
}

110
as.js
View File

@ -1,7 +1,73 @@
;(function(){
function as(el, gun, cb){
function as(el, gun, cb, opt){
el = $(el);
if(gun === as.gui && as.el && as.el.is(el)){ return }
opt = opt || {};
opt.match = opt.match || '{{ ';
opt.end = opt.end || ' }}';
;(function(){ // experimental
function nest(t, s,e, r, i,tmp,u){
if(r && !r.length){ return t||'' }
if(!t){ return [] }
e = e || s;
i = t.indexOf(s, i||0);
if(0 > i){ return [] }
tmp = t.indexOf(e, i+1);
if(!r){ return [t.slice(i+s.length, tmp)].concat(nest(t, s,e, r, tmp,tmp,u)) }
return t.slice(0,i)+r[0]+nest(t.slice(tmp+e.length), s,e, r.slice(1), 0,tmp,u);
}
/* experimental */
function template(tag, attr){
var html = (tag = $(tag))[0].outerHTML, sub, tmp;
if(html && (0 > html.indexOf(opt.match))){ return }
if(!attr){
$.each(tag[0].attributes, function(i,v){
if(!v){ return }
if(!nest(v.value, opt.match, opt.end).length){ return }
template(tag, v.name)
});
if((sub = tag.children()).length){
return sub.each(function(){ template(this) });
}
}
var data = [], plate = attr? tag.attr(attr) : tag.html();
tmp = nest(plate, opt.match, opt.end);
if(!tmp.length){ return }
$.each(tmp, function(pos, match){
var expr = match.split(' ');
var path = (expr[0]).split('.');
if(expr = expr.slice(1).join(' ')){
expr = new Function("_", "b", "return (_)" + expr);
}
var val = (expr && expr('')) || '';
data.push(val);
if(!attr){ tag.text(val) }
var ref = gun, sup = [], tmp;
if(tmp = tag.attr('name')){ sup.push(tmp) }
tag.parents("[name]").each(function(){
sup.push($(this).attr('name'));
});
$.each(path = sup.reverse().concat(path), function(i,v){
ref = ref.get(v);
});
ref.on(function(v){
v = data[pos] = expr? expr(v) : v;
var tmp = nest(plate, opt.match, opt.end, data);
if(attr){
tag.attr(attr, tmp);
} else {
tag.text(tmp);
}
});
});
}
template(el);
}());
as.gui = gun;
as.el = el;
if(el.data('as')){
@ -59,10 +125,12 @@
if(many && ui.is('.sort')){
var up = ui.closest("[name='#']");
var tmp = as.sort(data, up.parent().children().last());
up.insertAfter(tmp);
tmp? up.insertAfter(tmp) : up.prependTo(up.parent());
}
if(as.lock === gui){ return }
(ui[0] && u === ui[0].value)? ui.text(data) : ui.val(data);
if(!(data && data instanceof Object)){
(ui[0] && u === ui[0].value)? ui.text(data) : ui.val(data);
}
ui.data('was', data);
if(cb){
cb(data, key, ui);
@ -80,12 +148,7 @@
}, wait || 200);
}
}
as.sort = function sort(id, li){
var num = parseFloat(id);
var id = $(li).find('.sort').text() || -Infinity;
var at = num >= parseFloat(id);
return at ? li : sort(id, li.prev());
}
as.sort = function sort(num, li){ return parseFloat(num) >= parseFloat($(li).find('.sort').text() || -Infinity)? li : sort(num, li.prev()) }
$(document).on('keyup', 'input, textarea, [contenteditable]', as.wait(function(){
var el = $(this);
var data = (el[0] && u === el[0].value)? el.text() : el.val();
@ -94,7 +157,7 @@
as.lock = g;
g.put(data);
}, 99));
$(document).on('submit', 'form', function(e){ e.preventDefault() });
//$(document).on('submit', 'form', function(e){ e.preventDefault() });
var u;
window.as = as;
$.as = as;
@ -146,4 +209,31 @@
;$(function(){
$('.page').not(':first').hide();
$.as.route(location.hash.slice(1));
$(JOY.start = JOY.start || function(){ $.as(document, gun, null, JOY.opt) });
if($('body').attr('peers')){ (console.warn || console.log)('Warning: Please upgrade <body peers=""> to https://github.com/eraeco/joydb#peers !') }
});
;(function(){ // need to isolate into separate module!
var joy = window.JOY = function(){};
joy.auth = function(a,b,cb,o){
if(!o){ o = cb ; cb = 0 }
if(o === true){
gun.user().create(a, b);
return;
}
gun.user().auth(a,b, cb,o);
}
var opt = joy.opt = window.CONFIG || {}, peers;
$('link[type=peer]').each(function(){ (peers || (peers = [])).push($(this).attr('href')) });
!window.gun && (opt.peers = opt.peers || peers || (function(){
(console.warn || console.log)('Warning: No peer provided, defaulting to DEMO peer. Do not run in production, or your data will be regularly wiped, reset, or deleted. For more info, check https://github.com/eraeco/joydb#peers !');
return ['https://gunjs.herokuapp.com/gun'];
}()));
window.gun = window.gun || Gun(opt);
gun.on('auth', function(ack){
console.log("Your namespace is publicly available at", ack.soul);
});
}());

188
axe.js
View File

@ -1,99 +1,109 @@
;(function(){
/* UNBUILD */
var root;
if(typeof window !== "undefined"){ root = window }
if(typeof global !== "undefined"){ root = global }
root = root || {};
var console = root.console || {log: function(){}};
function USE(arg, req){
return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
arg(mod = {exports: {}});
USE[R(path)] = mod.exports;
}
function R(p){
return p.split('/').slice(-1).toString().replace('.js','');
}
}
if(typeof module !== "undefined"){ var common = module }
/* UNBUILD */
var sT = setTimeout || {}, u;
if(typeof window !== ''+u){ sT.window = window }
var AXE = (sT.window||'').AXE || function(){};
if(AXE.window = sT.window){ AXE.window.AXE = AXE }
;USE(function(module){
if(typeof window !== "undefined"){ module.window = window }
var tmp = module.window || module;
var AXE = tmp.AXE || function(){};
var Gun = (AXE.window||'').GUN || require('./gun');
(Gun.AXE = AXE).GUN = AXE.Gun = Gun;
if(AXE.window = module.window){ try{
AXE.window.AXE = AXE;
tmp = document.createEvent('CustomEvent');
tmp.initCustomEvent('extension', false, false, {type: "AXE"});
(window.dispatchEvent || window.fireEvent)(tmp);
window.postMessage({type: "AXE"}, '*');
} catch(e){} }
//if(!Gun.window){ try{ require('./lib/axe') }catch(e){} }
if(!Gun.window){ require('./lib/axe') }
try{ if(typeof common !== "undefined"){ common.exports = AXE } }catch(e){}
module.exports = AXE;
})(USE, './root');
;USE(function(module){
Gun.on('opt', function(at){ start(at) ; this.to.next(at) }); // make sure to call the "next" middleware adapter.
var AXE = USE('./root'), Gun = (AXE.window||{}).Gun || USE('./gun', 1);
(Gun.AXE = AXE).GUN = AXE.Gun = Gun;
function start(root){
if(root.axe){ return }
var opt = root.opt, peers = opt.peers;
if(false === opt.axe){ return }
if(!Gun.window){ return } // handled by ^ lib/axe.js
var w = Gun.window, lS = w.localStorage || opt.localStorage || {}, loc = w.location || opt.location || {}, nav = w.navigator || opt.navigator || {};
var axe = root.axe = {}, tmp, id;
var mesh = opt.mesh = opt.mesh || Gun.Mesh(root); // DAM!
Gun.on('opt', function(at){
if(!at.axe){
at.axe = {};
var p = at.opt.peers, tmp;
// 1. If any remembered peers or from last cache or extension
// 2. Fallback to use hard coded peers from dApp
// 3. Or any offered peers.
//if(Gun.obj.empty(p)){
// Gun.obj.map(['http://localhost:8765/gun'/*, 'https://guntest.herokuapp.com/gun'*/], function(url){
// p[url] = {url: url, axe: {}};
// });
//}
// Our current hypothesis is that it is most optimal
// to take peers in a common network, and align
// them in a line, where you only have left and right
// peers, so messages propagate left and right in
// a linear manner with reduced overlap, and
// with one common superpeer (with ready failovers)
// in case the p2p linear latency is high.
// Or there could be plenty of other better options.
console.log("axe", at.opt);
if(at.opt.super){
function verify(msg, send, at) {
var peers = Object.keys(p), puts = Object.keys(msg.put), i, j, peer;
var soul = puts[0]; /// TODO: verify all souls in puts. Copy the msg only with subscribed souls?
for (i=0; i < peers.length; ++i) {
peer = p[peers[i]];
//if (peer.url) {console.log('AXE do not reject superpeers'); send(msg, peer); continue;} /// always send to superpeers?
if (!peer.id) {console.log('AXE peer without id: ', peer); continue;}
if (!Gun.subscribe[soul] || !Gun.subscribe[soul][peer.id]) { console.log('AXE SAY reject msg to peer: %s, soul: %s', peer.id, soul); continue; }
send(msg, peer);
}
}
AXE.say = function(msg, send, at) {
if (!msg.put) { send(msg); return; }
console.log('AXE HOOK!! ', msg);
verify(msg, send, at);
};
/// TODO: remove peer from all Gun.subscribe. On `mesh.bye` event?
}
if(at.opt.super){
at.on('in', USE('./lib/super', 1), at);
} else {
//at.on('in', input, at);
}
}
this.to.next(at); // make sure to call the "next" middleware adapter.
});
tmp = peers[id = loc.origin + '/gun'] = peers[id] || {};
tmp.id = tmp.url = id; tmp.retry = tmp.retry || 0;
tmp = peers[id = 'http://localhost:8765/gun'] = peers[id] || {};
tmp.id = tmp.url = id; tmp.retry = tmp.retry || 0;
Gun.log.once("AXE", "AXE enabled: Trying to find network via (1) local peer (2) last used peers (3) a URL parameter, and last (4) hard coded peers.");
Gun.log.once("AXEWarn", "Warning: AXE is in alpha, use only for testing!");
var last = lS.peers || ''; if(last){ last += ' ' }
last += ((loc.search||'').split('peers=')[1]||'').split('&')[0];
function input(msg){
var at = this.as, to = this.to;
}
root.on('bye', function(peer){
this.to.next(peer);
if(!peer.url){ return } // ignore WebRTC disconnects for now.
if(!nav.onLine){ peer.retry = 1 }
if(peer.retry){ return }
if(axe.fall){ delete axe.fall[peer.url || peer.id] }
(function next(){
if(!axe.fall){ setTimeout(next, 9); return } // not found yet
var fall = Object.keys(axe.fall||''), one = fall[(Math.random()*fall.length) >> 0];
if(!fall.length){ lS.peers = ''; one = 'https://gunjs.herokuapp.com/gun' } // out of peers
if(peers[one]){ next(); return } // already choose
mesh.hi(one);
}());
});
module.exports = AXE;
})(USE, './axe');
root.on('hi', function(peer){ // TEMPORARY! Try to connect all peers.
this.to.next(peer);
if(!peer.url){ return } // ignore WebRTC disconnects for now.
return; // DO NOT COMMIT THIS FEATURE YET! KEEP TESTING NETWORK PERFORMANCE FIRST!
(function next(){
if(!peer.wire){ return }
if(!axe.fall){ setTimeout(next, 9); return } // not found yet
var one = (next.fall = next.fall || Object.keys(axe.fall||'')).pop();
if(!one){ return }
setTimeout(next, 99);
mesh.say({dam: 'opt', opt: {peers: one}}, peer);
}());
});
}());
function found(text){
axe.fall = {};
((text||'').match(/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/ig)||[]).forEach(function(url){
axe.fall[url] = {url: url, id: url, retry: 0}; // RETRY
});
return;
// TODO: Finish porting below? Maybe not.
Object.keys(last.peers||'').forEach(function(key){
tmp = peers[id = key] = peers[id] || {};
tmp.id = tmp.url = id;
});
tmp = peers[id = 'https://guntest.herokuapp.com/gun'] = peers[id] || {};
tmp.id = tmp.url = id;
var mesh = opt.mesh = opt.mesh || Gun.Mesh(root); // DAM!
mesh.way = function(msg){
if(root.$ === msg.$ || (msg._||'').via){
mesh.say(msg, opt.peers);
return;
}
var at = (msg.$||'')._;
if(!at){ mesh.say(msg, opt.peers); return }
if(msg.get){
if(at.axe){ return } // don't ask for it again!
at.axe = {};
}
mesh.say(msg, opt.peers);
}
}
if(last){ found(last); return }
try{ fetch(((loc.search||'').split('axe=')[1]||'').split('&')[0] || loc.axe || 'https://raw.githubusercontent.com/wiki/amark/gun/volunteer.dht.md').then(function(res){
return res.text()
}).then(function(text){
found(lS.peers = text);
}).catch(function(){
found(); // nothing
})}catch(e){found()}
}
var empty = {}, yes = true;
try{ if(typeof module != ''+u){ module.exports = AXE } }catch(e){}
}());

4
browser.js Normal file
View File

@ -0,0 +1,4 @@
// if(!(typeof navigator == "undefined") && navigator.product == "ReactNative"){
// require("./lib/mobile.js");
// }
module.exports = require('./gun.js');

217
examples/Main.js Normal file
View File

@ -0,0 +1,217 @@
import { render } from './iris/js/lib/preact.js';
import { Router, route } from './iris/js/lib/preact-router.es.js';
import { createHashHistory } from './iris/js/lib/history.production.min.js';
import { Component } from './iris/js/lib/preact.js';
import { Link } from './iris/js/lib/preact.match.js';
import Helpers from './iris/js/Helpers.js';
import { html } from './iris/js/Helpers.js';
import QRScanner from './iris/js/QRScanner.js';
import PeerManager from './iris/js/PeerManager.js';
import Session from './iris/js/Session.js';
import { translate as t } from './iris/js/Translation.js';
import Settings from './iris/js/views/Settings.js';
import LogoutConfirmation from './iris/js/views/LogoutConfirmation.js';
import Chat from './iris/js/views/Chat.js';
import Store from './iris/js/views/Store.js';
import Checkout from './iris/js/views/Checkout.js';
import Product from './iris/js/views/Product.js';
import Login from './iris/js/views/Login.js';
import Profile from './iris/js/views/Profile.js';
import Group from './iris/js/views/Group.js';
import Message from './iris/js/views/Message.js';
import Follows from './iris/js/views/Follows.js';
import Feed from './iris/js/views/Feed.js';
import About from './iris/js/views/About.js';
import Explorer from './iris/js/views/Explorer.js';
import Contacts from './iris/js/views/Contacts.js';
import Torrent from './iris/js/views/Torrent.js';
import VideoCall from './iris/js/components/VideoCall.js';
import Identicon from './iris/js/components/Identicon.js';
import MediaPlayer from './iris/js/components/MediaPlayer.js';
import Footer from './iris/js/components/Footer.js';
import State from './iris/js/State.js';
import Icons from './iris/js/Icons.js';
const userAgent = navigator.userAgent.toLowerCase();
const isElectron = (userAgent.indexOf(' electron/') > -1);
if (!isElectron && ('serviceWorker' in navigator)) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('iris/serviceworker.js')
.catch(function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
}
State.init();
Session.init({autologin: true});
PeerManager.init();
Helpers.checkColorScheme();
const APPLICATIONS = [ // TODO: move editable shortcuts to State.local gun
{url: '/', text: t('home'), icon: Icons.home},
{url: '/feed', text: t('feed'), icon: Icons.feed},
{url: '/media', text: t('media'), icon: Icons.play},
{url: '/settings', text: t('settings'), icon: Icons.settings},
{url: '/store', text: t('store'), icon: Icons.store},
{url: '/explorer', text: t('explorer'), icon: Icons.folder},
{url: '/chat', text: t('messages'), icon: Icons.chat},
// {url: '/store', text: t('store'), icon: Icons.store}, // restore when it works!
{},
{url: '../stats.html', text: 'Gun node stats'},
{url: '../iris/index.html', text: 'Iris', icon: html`<img src="iris/img/icon128.png" width=24/>`},
{url: '../infinite-scroll/index.html', text: 'Infinite scroll'},
{url: '../chat/index.html', text: 'Chat'},
{url: '../game/space.html', text: 'Space'},
{},
{url: 'https://gun.eco/docs/', text: 'Gun documentation'},
{url: 'https://examples.iris.to/components/', text: 'Iris web components'}
];
const HomeView = () => {
return html`
<div class="main-view">
<div class="centered-container public-messages-view">
<h1>Hello, world!</h1>
<p>Here you can find sample applications and utilities for <a href="https://github.com/amark/gun">GUN</a>.</p>
<p>If you need any help, please feel free to join the GUN community chat: <a href="http://chat.gun.eco">http://chat.gun.eco</a></p>
<a href="/explorer" class="msg"><div class="msg-content">
<b>Explorer</b>
<p>Explore the data saved on the GUN database. Open to the side while using an application and see the data change in real-time.</p>
</div></a>
<a class="msg" href="game/space.html"><div class="msg-content">
<div class="img-container"><img src="iris/img/space-game.jpg"/></div>
<b>Space</b>
<p>Spaceflight game. Open in 2 or more browser windows.</p>
</div></a>
<a class="msg" href="/iris/index.html"><div class="msg-content">
<div class="img-container"><img src="iris/img/screenshot.png"/></div>
<b>Iris</b>
<p>Decentralized Twitter/Instagram. Provides modular components that can be reused in other applications (including this one).</p>
</div></a>
<a native class="msg" href="/chat/index.html"><div class="msg-content">
<div class="img-container"><img src="iris/img/gun-chat.jpg"/></div>
<b>Chat</b>
<p>Shoutbox!</p>
</div></a>
</div>
</div>
`;
};
class MenuView extends Component {
componentDidMount() {
State.local.get('showMenu').on(showMenu => this.setState({showMenu}));
}
render() {
const pub = Session.getPubKey();
return html`
<div class="application-list ${this.state.showMenu ? 'menu-visible-xs' : ''}">
<a href="/profile/${pub}">
<span class="icon"><${Identicon} str=${pub} width=40/></span>
<span class="text" style="font-size: 1.2em;border:0;margin-left: 7px;"><iris-text user="${pub}" path="profile/name" editable="false"/></span>
</a>
<br/><br/>
${APPLICATIONS.map(a => {
if (a.url) {
return html`
<a href=${a.url}>
<span class="icon">${a.icon || Icons.circle}</span>
<span class="text">${a.text}</span>
</a>`;
} else {
return html`<br/><br/>`;
}
})}
</div>
`;
}
};
class Main extends Component {
constructor() {
super();
this.showMenu = false;
}
componentDidMount() {
State.local.get('loggedIn').on(loggedIn => this.setState({loggedIn}));
}
handleRoute(e) {
let activeRoute = e.url;
if (!activeRoute && window.location.hash) {
return route(window.location.hash.replace('#', '')); // bubblegum fix back navigation
}
document.title = 'Iris';
if (activeRoute && activeRoute.length > 1) { document.title += ' - ' + Helpers.capitalize(activeRoute.replace('/', '')); }
State.local.get('activeRoute').put(activeRoute);
QRScanner.cleanupScanner();
}
onClickOverlay() {
if (this.state.showMenu) {
this.setState({showMenu: false});
}
}
toggleMenu(show) {
this.setState({showMenu: typeof show === 'undefined' ? !this.state.showMenu : show});
}
render() {
const content = this.state.loggedIn ? html`
<div class="visible-xs-flex" style="border-bottom:var(--sidebar-border-right)">
<svg onClick=${() => State.local.get('showMenu').put(this.showMenu = !this.showMenu)} style="padding: 5px;cursor:pointer;" viewBox="0 -53 384 384" width="40px"><path d="m368 154.667969h-352c-8.832031 0-16-7.167969-16-16s7.167969-16 16-16h352c8.832031 0 16 7.167969 16 16s-7.167969 16-16 16zm0 0"/><path d="m368 32h-352c-8.832031 0-16-7.167969-16-16s7.167969-16 16-16h352c8.832031 0 16 7.167969 16 16s-7.167969 16-16 16zm0 0"/><path d="m368 277.332031h-352c-8.832031 0-16-7.167969-16-16s7.167969-16 16-16h352c8.832031 0 16 7.167969 16 16s-7.167969 16-16 16zm0 0"/></svg>
</div>
<section class="main" style="flex-direction: row;">
<${MenuView}/>
<div style="flex: 3; display: flex">
<${Router} history=${createHashHistory()} onChange=${e => this.handleRoute(e)}>
<${HomeView} path="/"/>
<${Feed} path="/feed"/>
<${Feed} path="/search/:term?/:type?"/>
<${Feed} path="/media" index="media"/>
<${Login} path="/login"/>
<${Chat} path="/chat/:id?"/>
<${Message} path="/post/:hash"/>
<${Torrent} path="/torrent/:id"/>
<${About} path="/about"/>
<${Settings} path="/settings"/>
<${LogoutConfirmation} path="/logout"/>
<${Profile} path="/profile/:id?" tab="profile"/>
<${Profile} path="/replies/:id?" tab="replies"/>
<${Profile} path="/likes/:id?" tab="likes"/>
<${Profile} path="/media/:id" tab="media"/>
<${Group} path="/group/:id?"/>
<${Store} path="/store/:store?"/>
<${Checkout} path="/checkout/:store?"/>
<${Product} path="/product/:product/:store"/>
<${Product} path="/product/new" store=Session.getPubKey()/>
<${Explorer} path="/explorer/:node"/>
<${Explorer} path="/explorer"/>
<${Follows} path="/follows/:id"/>
<${Follows} followers=${true} path="/followers/:id"/>
<${Contacts} path="/contacts"/>
</${Router}>
</div>
</section>
<${VideoCall}/>
` : '';
return html`
<div id="main-content">
${content}
</div>
`;
}
}
render(html`<${Main}/>`, document.body);
$('body').css('opacity', 1); // use opacity because setting focus on display: none elements fails

View File

@ -22,7 +22,7 @@
"@angular/router": "^4.1.0",
"core-js": "^2.4.1",
"express-http-proxy": "^1.0.1",
"gun": "^0.7.4",
"gun": "https://github.com/amark/gun.git#master",
"ngx-pipes": "^2.0.5",
"rxjs": "^5.3.0",
"underscore": "^1.8.3",

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@ import { pick } from 'underscore';
export function on$(node, cleanup = true): Observable<any> {
return Observable.fromEventPattern(
h => {
// there is no way to off() an on() until at least one value is trigerred
// there is no way to off() an on() until at least one value is triggered
// so that we can access the event listener to off() it
const signal = { stop: false };
node.on((data, key, at, ev) => {

File diff suppressed because it is too large Load Diff

View File

@ -8,8 +8,11 @@
</head>
<body>
<h3 id="pid"></h3>
<script src="../gun.js"></script>
<!-- <script src="../axe.js"></script> -->
<script src="../axe.js"></script>
<script src="../lib/radix.js"></script>
<script src="../lib/webrtc.js"></script>
<!-- <script src="../sea.js"></script> -->
<script>
var pid = location.hash.slice(1);
@ -23,14 +26,17 @@
Gun.on('opt', function(ctx) {
this.to.next(ctx);
ctx.on('hi', function(opt) {
console.log('HI!! PEER', new Date(), opt.pid);
// console.log('HI!! PEER', new Date(), opt.pid);
setTimeout(function() {
document.getElementById('pid').innerHTML = gun._.opt.pid;
});
});
if (pid) {
ctx.on('out', function(msg) {
msg.pid = pid;
this.to.next(msg);
});
}
// if (pid) {
// ctx.on('out', function(msg) {
// msg.pid = pid;
// this.to.next(msg);
// });
// }
});
var gun = Gun(opt);

21
examples/basic/chat.html Normal file
View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<ul id='list'></ul>
<form id='form'>
<input id='who' placeholder='name'>
<input id='what' placeholder='say'>
<input type='submit' value='send'>
</form>
<script src="../../../gun/gun.js"></script>
<script src="../../../gun/axe.js"></script>
<script src="https://cdn.jsdelivr.net/npm/emojione@4.0.0/lib/js/emojione.min.js"></script>
<script>
gun = GUN(), chat = gun.get("note" + location.hash.replace('#','/')), view = document;
form.onsubmit = (eve) => { chat.set(who.value+': '+what.value), eve.preventDefault(what.value = "") }
chat.map().on(function show(data, id){
(view.line = view.getElementById(id) || view.createElement("li")).id = id;
list.appendChild(view.line).innerText = emojione.shortnameToUnicode(data);
window.scroll(0, list.offsetHeight);
(list.beep = new SpeechSynthesisUtterance()).text = "new";
list.beep.rate = 10, list.beep.pitch = 2, window.speechSynthesis.speak(list.beep);
});
</script>

View File

@ -0,0 +1,2 @@
<!DOCTYPE html>
<p>Moved to <a href="./chat.html">./chat.html</a>!</p>

38
examples/basic/meet.html Normal file
View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<center>must press play or unmute on new videos to accept meeting</center>
<center id="videos">
<video id="me" width="100%" controls autoplay playsinline muted></video>
</center>
<center>Stream <select id="select"><option id="from">from</option></select></center>
<script src="../jquery.js"></script>
<script src="../../../gun/gun.js"></script>
<script src="../../../gun/sea.js"></script>
<script src="../../../gun/lib/webrtc.js"></script>
<script>;(async function(){
streams = {}, gun = Gun(location.origin + '/gun'); //gun = GUN();
mesh = gun.back('opt.mesh');
(await (me.stream = navigator.mediaDevices).enumerateDevices()).forEach((device,i) => {
if('videoinput' !== device.kind){ return }
var opt = $(from).clone().prependTo('select').get(0);
$(opt).text(opt.id = device.label || 'Camera '+i);
opt.value = device.deviceId;
});
$('select').on('change', async eve => { $(from).text('Off'); // update label
if('Off' == select.value){ return me.srcObject.getTracks()[0].stop() }
mesh.hi(me.srcObject = await me.stream.getUserMedia({ audio: true,
video: (select.value && {deviceId: {exact: select.value}}) || {facingMode: "environment"}
}));
});
gun.on('rtc', async function(eve){ var ui, src;
console.log("?RTC?", eve.peer && eve.peer.connectionState, eve);
if(!(src = eve.streams)){ return }
ui = $('#v'+(src=src[0]).id).get(0) || $(me).clone().attr('id', 'v'+src.id).prependTo('#videos').get(0); // reuse or create video element
ui.srcObject = src;
});
}());</script>

9
examples/basic/note.html Normal file
View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<style>html, body, textarea { width: 100%; height: 100%; padding: 0; margin: 0; }</style>
<textarea id="view" placeholder="write here..."></textarea>
<script src="../../../gun/gun.js"></script><script>
gun = GUN(location.origin + '/gun');
note = gun.get('note').get(location.hash.replace('#','')||1);
view.oninput = () => { note.put(view.value) };
note.on((data) => { view.value = data });
</script>

View File

@ -0,0 +1,2 @@
<!DOCTYPE html>
<p>Moved to <a href="./note.html">./note.html</a>!</p>

148
examples/basic/poll.html Normal file
View File

@ -0,0 +1,148 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<script src="../jquery.js"></script>
<script src="../../../gun/gun.js"></script>
<script src="../../../gun/sea.js"></script>
<!-- script src="../../../gun/axe.js"></script -->
<script> // main init!
var app = {
view: $, // replace with not jquery!
data: GUN('http://localhost:8765/gun'), // peer-to-peer database!
};
app.user = app.data.user().recall({sessionStorage: true});
</script>
</head>
<body>
<div id="login" class="center pad">
<style>
#login input {
max-width: 6em;
}
</style>
<form id="sign" onsubmit="app.login(event)">
<input id="alias" placeholder="username" class="jot rim">
<input id="pass" type="password" placeholder="passphrase" class="jot rim">
<input id="in" type="submit" value="sign in" class="green whitet act gap sap rim">
<input id="up" type="button" value="sign up" onclick="app.register()" class="act gap sap rim">
</form>
<script>
app.login = function(eve){
if(app.error(eve)){ return }
app.data.user().auth(
app.view('#alias').val(),
app.view('#pass').val(),
app.error
);
};
app.register = function(eve){
app.data.user().create(
app.view('#alias').val(),
app.view('#pass').val(),
app.login
);
};
app.data.on('auth', function(eve){
app.view('#sign').hide(); // hide login form upon logging in.
});
</script>
</div>
<div id="poll" class="pad">
<style>
#poll {
display: flex;
flex-wrap: wrap;
}
#poll div {
margin: 1%;
width: 100%;
}
</style>
<script>
(window.onhashchange = async function(){
app.poll = app.data.get(location.hash.slice(1));
app.poll.map().on(function(data, id){
app.render(id = 'p'+String.hash(id), '.q', '#poll', data).css({order: data.how}).data('as',{$:this});
console.log("poll?", id, data);
});
})();
app.render = function(id, model, onto, data){
var ui = $(
$('#'+id).get(0) ||
$('.model').find(model).clone(true).attr('id', id).appendTo(onto)
);
$.each(data, function(field, val){
if($.isPlainObject(val)){ return }
ui.find("[name='" + field + "']").val(val).text(val);
});
return ui;
}
</script>
<div class="model">
<div class="q">
<span name="what"></span>
</div>
</div>
</div>
<div id="make" class="pad">
<style>
#make #add {
border-radius: 100%;
width: 2em;
height: 2em;
line-height: 0em;
padding: 0;
margin: 0;
text-align: center;
}
</style>
<button id="add" onclick="app.add()" class="green whitet act">+</button>
<span class="hint">add new title, text, question...</span>
<script>
app.add = async function(){
if(app.error(app.user)){ return }
var tmp = await (app.poll = app.poll || app.data.get(location.hash.slice(1)));
if(!tmp){ app.poll = app.user.get('poll').set({}) }
app.poll.set({how: tmp = Object.keys(tmp||'').length || 1, what: "Question " + tmp });
if(!location.hash){ location.hash = (await app.poll)._['#'] }
}
</script>
</div>
<span id="error">
<span id="err"></span>
<script>
app.error = function(eve){
app.view('#err').text('').hide();
if(!eve){ return }
if(eve.preventDefault){
eve.preventDefault();
return;
}
if(eve._ && !eve.is){ eve = {err: "Not signed in!"} }
if(!eve.err){ return }
app.view('#err').text(eve.err).show();
return true;
}
</script>
</span>
<style>
#error { position: fixed; top: 0; width: 100%; text-align: center; background: white; }
</style>
<link rel="stylesheet" href="../style.css"/>
<style>
@import url('https://fonts.googleapis.com/css?family=Oxygen');
html, body { font-family: "Oxygen", sans-serif; }
</style>
</body>
</html>

45
examples/basic/post.html Normal file
View File

@ -0,0 +1,45 @@
<!DOCTYPE html>
<h1>Posts</h1>
<form id="sign">
<input id="alias" placeholder="username">
<input id="pass" type="password" placeholder="passphrase">
<input id="in" type="submit" value="sign in">
<input id="up" type="button" value="sign up">
</form>
<form id="said">
<input id="say" placeholder="write here...">
<input id="speak" type="submit" value="say">
</form>
<ul></ul>
<script src="../jquery.js"></script>
<script src="../../../gun/gun.js"></script>
<script src="../../../gun/sea.js"></script>
<script src="../../../gun/axe.js"></script>
<script>
gun = GUN(), user = gun.user().recall({sessionStorage: true});
$('#sign').on('submit', login);
$('#up').on('click', () => { user.create($('#alias').val(), $('#pass').val(), login) });
function login(eve){
eve.preventDefault();
user.auth($('#alias').val(), $('#pass').val());
};
gun.on('auth', () => { $('#sign').hide(), user.get('said').map().on(show) });
function show(data, id){
return ($('#' + id).get(0) || $('<li>').attr('id', id).prependTo('ul')).text(data);
};
$('#said').on('submit', (eve) => {
eve.preventDefault();
if(!user.is){ return }
user.get('said').set($('#say').val());
$('#say').val("");
});
</script>

View File

@ -1,4 +1,4 @@
<html><body>
<!DOCTYPE html>
<style>
html, body {
background: rgb(245, 245, 245);
@ -70,13 +70,98 @@
Public Key: <input id="pub">
</div></div>
<script src="https://cdn.jsdelivr.net/npm/gun/examples/jquery.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/sea.js"></script>
<script src="../jquery.js"></script>
<script src="../../../gun/gun.js"></script>
<script src="../../../gun/sea.js"></script>
<script>
//var gun = Gun();
var gun = Gun('http://localhost:8080/gun');
// extend SEA functions to base64 encode encrypted data
// workaround for https://github.com/amark/gun/issues/783
(() => {
const _encrypt = SEA.encrypt;
SEA.encrypt = function(...args) {
return _encrypt.apply(this, args).then(enc => btoa(JSON.stringify(enc)));
}
const _decrypt = SEA.decrypt;
SEA.decrypt = function(data, ...args) {
try { data = JSON.parse(atob(data)); }
finally { return _decrypt.apply(this, [data, ...args]); }
}
})();
// override User functions to fix several issues
// see https://github.com/amark/gun/issues/808
SEA.Gun.User.prototype.grant = function grant(to, cb) {
const gun = this; const user = gun.back(-1).user();
const pair = user._.sea; let path = '';
gun.back(at => { if (at.has) { path += at.get; } });
(async () => {
let enc, sec;
if (sec = await user.get('trust').get(pair.pub).get(path).then()) {
sec = await SEA.decrypt(sec, pair);
} else {
sec = SEA.random(24).toString();
enc = await SEA.encrypt(sec, pair);
user.get('trust').get(pair.pub).get(path).put(enc);
}
let pub = to.get('pub') .then();
let epub = to.get('epub').then();
pub = await pub; epub = await epub;
const dh = await SEA.secret (epub, pair);
enc = await SEA.encrypt(sec, dh);
// if pub is not already in trust, first put an empty node
// workaround for https://github.com/amark/gun/issues/844
if (!await user.get('trust').get(pub).then()) {
await user.get('trust').get(pub).get(path).put({}).then();
}
user.get('trust').get(pub).get(path).put(enc, cb);
})();
return gun;
}
SEA.Gun.User.prototype.secret = function(data, cb) {
const gun = this; const user = gun.back(-1).user();
const pair = user._.sea; let path = '';
gun.back(at => { if (at.has) { path += at.get; } });
(async () => {
let enc, sec;
if (sec = await user.get('trust').get(pair.pub).get(path).then()) {
sec = await SEA.decrypt(sec, pair);
} else {
sec = SEA.random(24).toString();
enc = await SEA.encrypt(sec, pair);
user.get('trust').get(pair.pub).get(path).put(enc);
}
enc = await SEA.encrypt(data, sec);
gun.put(enc, cb);
})();
return gun;
}
var gun = Gun('http://localhost:8765/gun');
var user = gun.user();
var LI = {};
@ -94,7 +179,7 @@ $('#sign').on('submit', function(e){
gun.on('auth', function(){
$('#sign').hide();
$('#profile').show();
var pub = user.pair().pub;
var pub = user._.sea.pub;
$('#pub').val(pub);
return;
$("#search").val(pub).trigger('blur');
@ -126,15 +211,25 @@ $('#search').on('blur', function(e){
ev.off();
return;
}
Gun.node.is(data, async function(v, k){
if(k === LI.busy){ return }
var key = await find.get('trust').get(user.pair().pub).get(k+'profile').then();
var mix = await Gun.SEA.secret(await find.get('epub').then(), user.pair());
key = await Gun.SEA.decrypt(key, mix);
var val = await Gun.SEA.decrypt(v, key);
$('#'+k).val(val || v);
Gun.node.is(data, async (enc, id) => {
if (id === LI.busy) { return; }
const pair = user._.sea;
let key, val;
if (key =
await find.get('trust').get(pair.pub).get(id + 'profile').then()) {
const mix = await Gun.SEA.secret(await find.get('epub').then(), pair);
key = await Gun.SEA.decrypt(key, mix);
val = await Gun.SEA.decrypt(enc, key);
// decode encrypted data to show 'SEA{...}'
} else { val = JSON.parse(atob(enc)); }
$('#' + id).val(val);
});
});
});
</script>
</body></html>
</script>

View File

@ -0,0 +1,121 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
<link href='https://fonts.googleapis.com/css?family=Poiret+One' rel='stylesheet' type='text/css'>
</head>
<body>
<h1><button id="left">&larr;</button> <span id="date"></span> Schedule <button id="right">&rarr;</button></h1>
<form id="add">
<style>input[type="number"]{ width: 4em; }</style>
<input id="what" placeholder="What?">
<input id="where" placeholder="Where?">
<input type="number" id="hour"><script>hour.value = new Date().getHours() % 12 || 12</script> :
<input type="number" id="min" value="0">
<select id="ampm">
<option value="">am</option>
<option value="1">pm</option>
<script>ampm.children[new Date().getHours() < 12? 0 : 1].selected='selected'</script>
</select>
<input id="id" type="hidden">
<input id="go" type="submit" value="add">
<div id="err"></div>
</form>
<style>
.none { display: none; }
p, ul, li { list-style-type: none; margin: 0; padding: 0; }
</style>
<ul></ul>
<div class="model none">
<li>
<b class="when"></b>
<span class="what"></span>
<u class="where"></u>
<span class="sort none">0</span>
<button class="edit"><</button>
</li>
</div>
<script src="../jquery.js"></script>
<script src="../../../gun/gun.js"></script>
<script src="../../../gun/nts.js"></script>
<script src="../../../gun/lib/webrtc.js"></script>
<script>
var name = 'schedule/' + location.hash.slice(1);
var gun = Gun(location.origin + '/gun');
//var gun = Gun('http://localhost:8765/gun');
//var gun = Gun();
$('#add').on('submit', function(event){
event.preventDefault();
event = {};
if(!schedule.on){ return err.innerText = "No date!" }
event.when = new Date(schedule.on.getFullYear(), schedule.on.getMonth(), schedule.on.getDate(), hour.value % 12 + (ampm.value? 12 : 0), min.value).getTime();
if(!(event.what = what.value)){ return err.innerText = "No description!" }
if(!(event.where = where.value)){ return err.innerText = "No location!" }
var day = gun.get(name+now(event.when));
day.get(id.value || String.random(9)).put(event);
what.value = where.value = id.value = err.innerText = '';
go.value = 'add';
schedule(event.when);
});
function schedule(ms){
var day = new Date(ms);
if(schedule.on && schedule.on.toLocaleDateString() === day.toLocaleDateString()){ return } schedule.on = day;
$('#date').text(day.getFullYear()+' '+ day.toString().split(' ')[1] +' '+day.getDate());
day = gun.get(name+now(ms));
$('ul').empty();
day.map().on(UI);
}
schedule(+new Date());
$('#left').on('click', function(){ schedule(+new Date(schedule.on.getFullYear(), schedule.on.getMonth(), schedule.on.getDate() - 1)) });
$('#right').on('click', function(){ schedule(+new Date(schedule.on.getFullYear(), schedule.on.getMonth(), schedule.on.getDate() + 1)) });
function UI(event, id){
if(!event){ return }
var when = new Date(event.when);
if(schedule.on && when.toLocaleDateString() !== schedule.on.toLocaleDateString()){ return }
var ul = $('ul')
var li = $("#cal-" + id)[0]; // grab if exists
if(!li){
li = $('.model li').clone(true) // else create it
.attr('id', 'cal-' + id);
}
li = (UI.last = sort(event.when, ul.children('li').last())[0])? $(li).insertAfter(UI.last) : $(li).prependTo(ul);
li.find('.what').text(event.what);
li.find('.where').text(event.where);
li.find('.sort').text(event.when);
li.find('.edit').val(id);
var time = when.toLocaleTimeString();
li.find('.when').text(time.split(':').slice(0,2).join(':') + time.slice(-2));
};
$(document).on('click', '.edit', function(){
go.value = 'update';
id.value = this.value;
what.value = $(this).parent().find('.what').text();
where.value = $(this).parent().find('.where').text();
var when = new Date(parseFloat($(this).parent().find('.sort').text()));
hour.value = when.getHours() % 12 || 12;
min.value = when.getMinutes();
ampm.value = when.getHours() < 12? '' : 1;
what.focus();
});
function now(t){
return new Date(t || Gun.state()).toLocaleDateString().split('/').reverse().join('/')
}
function sort(num, li){ return parseFloat(num) >= parseFloat($(li).find('.sort').text() || -Infinity)? li : sort(num, li.prev()) }
</script>
</body>
</html>

View File

@ -0,0 +1,59 @@
<!DOCTYPE html>
<video id="video" width="100%"></video>
<center>
<button id="record">Record</button>
<button id="play">Play</button>
</center>
<script src="../jquery.js"></script>
<script src="../../../gun/gun.js"></script>
<script>
var gun = Gun(location.origin + '/gun');
var record = {recorder: null, recording: false};
$('#record').on('click', ()=>{
if(!record.ing){ return record.stream() }
$('#record').text("Record");
if(record.ing.stop){ record.ing.stop() }
record.ing = false;
})
record.stream = function(){
navigator.mediaDevices.getDisplayMedia({ video: true }).then(stream => {
var chunks = []; // we have a stream, we can record it
record.ing = new MediaRecorder(stream);
record.ing.ondataavailable = eve => chunks.push(eve.data);
record.ing.onstop = eve => record.save(new Blob(chunks));
record.ing.start()
$('#record').text("End");
}, err => { console.log(err) });
}
record.save = function(data){
record.file = record.file || new FileReader();
record.file.readAsDataURL(data);
record.file.onloadend = function(){
var b64 = record.file.result;
b64 = "data:video/webm" + b64.slice(b64.indexOf(';'));
gun.get('test').get('screen').put(b64);
}
}
$('#play').on('click', ()=>{
if(record.playing){
$('#play').text("Play")
$('#video').get(0).stop();
record.playing = false;
return;
}
$('#play').text("Stop");
record.playing = true;
gun.get('test').get('screen').once((data)=>{
if(!data){ return }
$('#video').get(0).src = data;
$('#video').get(0).play()
})
})
</script>

View File

@ -1,50 +0,0 @@
<h1>Search</h1>
<form id="ask">
<input id="search" placeholder="search..." autocomplete="off">
</form>
<div id="answer"></div>
<ul></ul>
<small>Note: No data is indexed by default, you need to add some!</small>
<script src="../../examples/jquery.js"></script>
<script src="../../gun.js"></script>
<script src="../../sea.js"></script>
<script src="../../lib/space.js"></script>
<script>
var gun = Gun();
var ask = {};
$('#search').on('keyup', function(e){
ask.now = (this.value||'').toLowerCase().replace(/[\W_]+/g,"");
if(ask.last === ask.now){ return }
ask.last = ask.now;
clearTimeout(ask.to);
ask.to = setTimeout(search, 20);
});
function search(){
var key = ask.now;
gun.get('Q').space(key, function(ack){
if(!ack || key !== ask.now){ return }
UI(ack)
});
}
function UI(ack){
$('#answer').text(ack.data || '');
var $ul = $('ul').empty(), tree = ack.tree;
Gun.obj.map(tree, function(v,k){
$('<li>').text(k +' - ' + v).appendTo($ul);
});
};
function load(DATA){
Gun.obj.map(DATA, function(v,k){
gun.get('Q').space(k, v);
});
}
</script>

107
examples/basic/stream.html Normal file
View File

@ -0,0 +1,107 @@
<!DOCTYPE html>
<center>
<img id="img" width="100%"><br/>
Stream <select id="select"><option id="from">from</option></select>
add <input id="pass" placeholder="password" type="password">
resolution <input id="res" value="240" step="32" max="1080" type="number" style="width:3em;">
or <input id="upload" type="file">
</center>
<video id="video" width="100%" controls autoplay style="display: none;"></video>
<canvas id="canvas" width="0" style="display: none;"></canvas>
<script src="../jquery.js"></script>
<script src="../../../gun/gun.js"></script>
<script src="../../../gun/sea.js"></script>
<script src="../../../gun/lib/webrtc.js"></script>
<script>;(async function(){
gun = Gun(location.origin + '/gun'); //gun = GUN();
stream = canvas.getContext('2d'), stream.from = navigator.mediaDevices;
(await stream.from.enumerateDevices()).forEach((device,i) => {
if('videoinput' !== device.kind){ return }
var opt = $(from).clone().prependTo('select').get(0);
$(opt).text(opt.id = device.label || 'Camera '+i);
opt.value = device.deviceId;
});
$('select').on('change', async eve => { $(from).text('Off'); // update label
if('Off' == select.value){ return video.srcObject.getTracks()[0].stop() }
video.srcObject = await stream.from.getUserMedia({ audio: false,
video: (select.value && {deviceId: {exact: select.value}}) || {facingMode: "environment"}
});
});
$('#upload').on('change', async eve => { console.log("Check ./upload.html") })
setInterval(async tmp => {
if(!(video.srcObject||'').active){ return }
var size = parseInt(res.value);
stream.drawImage(video, 0,0,
canvas.width = size || video.videoWidth * 0.1,
canvas.height = (size * (video.videoHeight/video.videoWidth)) || video.videoHeight * 0.1
);
var b64 = canvas.toDataURL('image/jpeg');
if(pass.value){ b64 = await SEA.encrypt(b64, pass.value) }
gun.get('test').get('video').put(b64);
}, 99);
gun.get('test').get('video').on(async data => {
if(pass.value){ data = await SEA.decrypt(data, pass.value) }
img.src = data; // Beware: Some browsers memory leak fast src updates.
});
// === AUDIO STREAMING WITH FADE-IN/OUT ===
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const mic = await stream.from.getUserMedia({ audio: true });
const src = audioCtx.createMediaStreamSource(mic);
const proc = audioCtx.createScriptProcessor(2048, 1, 1);
src.connect(proc);
proc.connect(audioCtx.destination);
proc.onaudioprocess = async e => {
const input = e.inputBuffer.getChannelData(0);
const output = new Float32Array(input.length);
const fade = 128;
for (let i = 0; i < fade; i++) {
output[i] = input[i] * (i / fade); // fade in
}
for (let i = fade; i < input.length - fade; i++) {
output[i] = input[i]; // middle
}
for (let i = input.length - fade; i < input.length; i++) {
output[i] = input[i] * ((input.length - i) / fade); // fade out
}
const int16 = new Int16Array(output.length);
for (let i = 0; i < output.length; i++) {
int16[i] = Math.max(-32768, Math.min(32767, output[i] * 32768));
}
let b64 = btoa(String.fromCharCode(...new Uint8Array(int16.buffer)));
if(pass.value){ b64 = await SEA.encrypt(b64, pass.value) }
gun.get('test').get('audio').put(b64);
};
gun.get('test').get('audio').on(async data => {
if(!data) return;
if(pass.value){ data = await SEA.decrypt(data, pass.value) }
const bin = atob(data);
const bytes = new Uint8Array(bin.length);
for(let i=0; i<bin.length; i++){ bytes[i] = bin.charCodeAt(i) }
const buf = audioCtx.createBuffer(1, bytes.length / 2, 44100);
const chan = buf.getChannelData(0);
for(let i=0; i<chan.length; i++){
const s = (bytes[i*2+1] << 8) | bytes[i*2];
chan[i] = (s > 32767 ? s - 65536 : s) / 32768;
}
const player = audioCtx.createBufferSource();
player.buffer = buf;
player.connect(audioCtx.destination);
player.start();
});
}());</script>

View File

@ -1,3 +1,5 @@
<!DOCTYPE html>
<h1>Tables</h1>
<form id="sign">

View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<div class="model" style="display: none;">
<video width="100%" controls autoplay></video>
<audio width="100%" controls autoplay></audio>
<img style="max-width: 100%;">
</div>
<center>
<p>Drag & drop videos, songs, or images! <input id="upload" type="file" multiple></p>
</center>
<script src="../../../gun/lib/yson.js"></script>
<script src="../../../gun/gun.js"></script>
<script src="../../../gun/lib/dom.js"></script>
<script src="../../../gun/lib/upload.js"></script>
<script>
gun = GUN(location.origin + '/gun');
$('html, #upload').upload(function resize(eve, up){
if(up){ return up.shrink(eve, resize, 1024) }
var b64 = (eve.base64 || ((eve.event || eve).target || eve).result || eve); // which one? try all!
gun.get('test').get((eve.id+(new Date).getUTCSeconds()) % 60).put(b64); // limit uploads to 1 of 60 slots.
});
gun.get('test').map().once(function(data){
if("string" != typeof data){ return }
var type = data.split(';')[0], ui;
if(type.indexOf('image') + 1){ ui = $("img").get(0) }
if(type.indexOf('video') + 1){ ui = $('video').get(0) }
if(type.indexOf('audio') + 1){ ui = $('audio').get(0) }
if(!ui){ return }
$(ui).clone().prependTo('center').get(0).src = data;
});
</script>

View File

@ -1,52 +1,2 @@
<h1>User</h1>
<form id="sign">
<input id="alias" placeholder="username">
<input id="pass" type="password" placeholder="passphrase">
<input id="in" type="submit" value="sign in">
<input id="up" type="button" value="sign up">
<input id="mask" type="button" value="Identifi Login">
</form>
<ul></ul>
<form id="said">
<input id="say">
<input id="speak" type="submit" value="speak">
</form>
<script src="../jquery.js"></script>
<script src="../../../gun/gun.js"></script>
<script src="../../../gun/sea.js"></script>
<script>
var gun = Gun(); //Gun(['http://localhost:8765/gun', 'https://guntest.herokuapp.com/gun']);
var user = gun.user().recall({sessionStorage: true});
$('#up').on('click', function(e){
user.create($('#alias').val(), $('#pass').val(), login);
});
function login(e){
user.auth($('#alias').val(), $('#pass').val());
return false; // e.preventDefault();
};
$('#sign').on('submit', login);
$('#mask').on('click', login);
gun.on('auth', function(){
$('#sign').hide();
user.get('said').map().on(UI);
});
$('#said').on('submit', function(e){
e.preventDefault();
//if(!user.is){ return }
user.get('said').set($('#say').val());
$('#say').val("");
});
function UI(say, id){
var li = $('#' + id).get(0) || $('<li>').attr('id', id).appendTo('ul');
$(li).text(say);
};
</script>
<!DOCTYPE html>
<p>Moved to <a href="./post.html">./post.html</a>!</p>

62
examples/basic/video.html Normal file
View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<video id="video" width="100%" controls autoplay></video>
<center>
<input id="pass" placeholder="password">
Record <button class="record">Camera</button> or <button class="record">Screen</button>
</center>
<script src="../jquery.js"></script>
<script src="../../../gun/gun.js"></script>
<script src="../../../gun/sea.js"></script>
<script>
var gun = Gun(location.origin + '/gun');
gun.get('test').get('video').on(async function(data){
if($('#pass').val()){ data = await SEA.decrypt(data, $('#pass').val()) }
$('#video').get(0).src = data;
})
$('.record').on('click', function(){
if(record.ing){
if(record.ing.stop){ record.ing.stop() }
$(this).text(record.type);
record.ing = false;
return;
}
record(record.type = $(this).text());
$(this).text("End");
})
function record(type){
if('Camera' === type){
navigator.getMedia({ video: true, audio: true }, load, error);
}
if('Screen' === type){
navigator.mediaDevices.getDisplayMedia({ video: true, audio: true }).then(load, error);
}
function load(media){
var chunks = [];
record.ing = new MediaRecorder(media);
record.ing.ondataavailable = function(eve){ chunks.push(eve.data) }
record.ing.onstop = function(eve){record.save(new Blob(chunks)) }
record.ing.start();
}
function error(err){ console.log(err) }
}
record.save = function(data){
record.file = record.file || new FileReader();
record.file.readAsDataURL(data);
record.file.onloadend = async function(){
var b64 = record.file.result, pass;
b64 = $('#video').get(0).src = "data:video/webm" + b64.slice(b64.indexOf(';'));
if($('#pass').val()){ b64 = await SEA.encrypt(b64, $('#pass').val()) }
gun.get('test').get('video').put(b64);
}
}
navigator.getMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia
|| navigator.mozGetUserMedia || navigator.msGetUserMedia);
</script>

View File

@ -1,161 +1,213 @@
<!DOCTYPE html>
<html>
<head>
<title>Converse</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
<link rel="stylesheet" type="text/css" href="/style.css">
<link href='https://fonts.googleapis.com/css?family=Poiret+One' rel='stylesheet' type='text/css'>
<style>
#converse {
font-size: 16pt;
}
#converse .box {
margin-bottom: 0.2em;
padding: 1em;
border-radius: 0.1em;
}
#converse b:after {
content: " ";
}
#converse li .when {
position: absolute;
top: 0;
right: -2em;
padding: 0.5em 1em;
background: rgba(100%,100%,100%,0.9);
opacity: 0;
}
#converse li:hover .when {
opacity: 1;
right: 0em;
}
.poiret {
font-family: 'Poiret One', sans-serif;
}
.large {
font-size: 200%;
}
#converse .what, #converse .who {
cursor: text;
outline: none;
display: inline;
min-width: 1em;
padding-left: 1px;
}
[contentEditable=true]:empty:not(:focus):before{
content:attr(data-text)
}
#title {
margin-bottom: 0.5em;
}
<head>
<title>Converse</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
<link rel="stylesheet" type="text/css" href="/style.css">
<link href='https://fonts.googleapis.com/css?family=Poiret+One' rel='stylesheet' type='text/css'>
<style>
.chat__heading {
position: fixed;
text-align: center;
z-index: 1;
width: 100%;
margin-top: 0;
margin-bottom: 0;
}
.say {
margin: 0 0 0.4em 0.4em;
padding: 0.2em 0.5em;
}
.chat__form-container {
display: flex;
justify-content: center;
width: 100%;
padding: 10px 20px;
position: fixed;
z-index: 1;
bottom: 0;
}
#name-input {
margin-top: 0.5em;
margin-right: 0.5em;
}
.chat__form {
display: flex;
justify-content: center;
height: 50px;
background-color: white;
border: 2px solid white;
max-width: 900px;
width: 100%;
border-radius: 5px;
}
#message-input {
margin-top: 0.5em;
}
</style>
</head>
<body>
<div id="converse" class="hue2 page">
<div class="pad">
<div id='title' class="poiret large rubric whitet">Have a Conversation...</div>
<div>
<ul>
<li class="none"></li>
</ul>
.chat__name-input {
flex: 1;
padding: 10px;
}
<form class="white huet2 box">
<div>
<div class="say hue2 right whitet box act">say</div>
<b id="name-input" class="jot left who" contenteditable="true" data-text="Name"></b>
<p id="message-input" class="jot left what" contenteditable="true" data-text="Write a message..."></p>
</div>
</form>
.chat__message-input {
flex: 5;
padding: 10px;
}
<div class="model">
<li class="white huet2 box">
<b class="who"></b>
<p class="what"></p>
<span class="sort none">0</span>
<div class="when"></div>
</li>
</div>
</div>
</div>
</div>
.chat__submit {
padding: 10px;
color: white;
border-radius: 5px;
}
<script src="/jquery.js"></script>
<script src="/gun.js"></script>
<script src="/gun/nts.js"></script>
<script>
var gun = Gun(location.origin + '/gun');
var chat = gun.get('converse/' + location.hash.slice(1));
.chat__submit:hover::after {
background-color: rgba(0,0,0,0.2);
}
$("form .say").on('click', submit);
$("form .what").on('keydown', enter);
function enter(e){
if(e.which !== 13){ return }
submit(e);
}
function submit(e){
e.preventDefault();
.chat__submit:focus::after {
background-color: rgba(0,0,0,0.2);
}
var msg = {when: Gun.time.is()};
.chat__submit::after {
content: '';
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
border-radius: 5px;
transition: background-color 0.3s;
background-color: rgba(0,0,0,0);
}
msg.who = $('form .who').text();
if(!msg.who){
msg.who = 'user' + Gun.text.random(3);
$('form .who').text(msg.who);
}
.chat__message-list {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
overflow-y: auto;
padding: 60px 20px;
width: 100%;
background-color: rgba(0, 0, 0, 0.2);
min-height: 100vh;
}
msg.what = $('form .what').text();
if(!msg.what){ return }
.chat__message {
display: flex;
flex-wrap: wrap;
margin-bottom: 10px;
padding: 10px;
border-radius: 5px;
width: 100%;
position: relative;
max-width: 900px;
}
chat.set(msg);
$('form .what').text('');
}
.chat__name {
margin-right: 20px;
}
chat.map().val(function(msg, id){
if(!msg){ return }
var ul = $('ul');
var last = sort(msg.when, ul.children('li').last());
.chat__when {
position: absolute;
top: 0;
right: 2em;
padding: 10px;
background: rgba(100%, 100%, 100%, 0.9);
opacity: 0;
border-radius: 5px;
}
var li = $("#msg-" + id)[0]; // grab if exists
if(!li){
li = $('.model li').clone(true) // else create it
.attr('id', 'msg-' + id)
.insertAfter(last);
}
.chat__message:hover .chat__when {
opacity: 1;
right: 0em;
}
// bind the message data into the UI
li = $(li);
li.find('.who').text(msg.who);
li.find('.what').text(msg.what);
li.find('.sort').text(msg.when);
@media (max-width: 567px) {
.chat__heading {
font-size: 30px;
}
}
</style>
</head>
var time = new Date(msg.when);
li.find('.when').text(time.toDateString() + ', ' + time.toLocaleTimeString());
<body>
<div class="chat hue2 page">
<h2 id='title' class="chat__heading hue2 whitet">Have a Conversation...</h2>
<ul class="chat__message-list">
<li class="none"></li>
</ul>
$('html, body').stop(true, true)
.animate({scrollTop: ul.height()});
});
<div class="chat__form-container hue2">
<form class="chat__form">
<label for="name-input" class="visually-hidden">Name</label>
<input id="name-input" class="chat__name-input" placeholder="Name"></input>
<label for="message-input" class="visually-hidden">Message</label>
<input id="message-input" class="chat__message-input" placeholder="Write a message..."></input>
<button class="chat__submit say hue2">say</button>
</form>
</div>
function sort(id, li){
var num = parseFloat(id);
var id = $(li).find('.sort').text() || -Infinity;
var at = num >= parseFloat(id);
return at ? li : sort(id, li.prev());
}
</script>
</body>
</html>
<div class="model">
<li class="chat__message white huet2 box">
<b class="chat__name"></b>
<p class="chat__message-text"></p>
<span class="sort none">0</span>
<div class="chat__when"></div>
</li>
</div>
</div>
<script src="/jquery.js"></script>
<script src="/gun.js"></script>
<script src="/gun/nts.js"></script>
<script>
var gun = Gun(location.origin + '/gun');
var chat = gun.get('converse/' + location.hash.slice(1));
$(".chat__submit").on('click', submit);
$(".chat_form").on('keydown', enter);
function enter(e) {
if (e.which !== 13) { return }
submit(e);
}
function submit(e) {
e.preventDefault();
var msg = { when: Gun.state() };
msg.who = $('.chat__name-input').val();
if (!msg.who) {
msg.who = 'user' + String.random(3);
$('.chat__name-input').val(msg.who);
}
msg.what = $('.chat__message-input').val();
if (!msg.what) { return }
chat.set(msg);
$('.chat__message-input').val('').focus();
}
chat.map().once(function (msg, id) {
if (!msg) { return }
var messageList = $('.chat__message-list');
var last = sort(msg.when, messageList.children('li').last());
var li = $("#msg-" + id)[0]; // grab if exists
if (!li) {
li = $('.model li').clone(true) // else create it
.attr('id', 'msg-' + id)
.insertAfter(last);
}
// bind the message data into the UI
li = $(li);
li.find('.chat__name').text(msg.who);
li.find('.chat__message-text').text(msg.what);
li.find('.sort').text(msg.when);
var time = new Date(msg.when);
li.find('.chat__when').text(time.toDateString() + ', ' + time.toLocaleTimeString());
$('html, body').stop(true, true)
.animate({ scrollTop: messageList.height() });
});
function sort(num, li) { return parseFloat(num) >= parseFloat($(li).find('.sort').text() || -Infinity) ? li : sort(num, li.prev()) }
</script>
</body>
</html>

379
examples/docs.html Normal file
View File

@ -0,0 +1,379 @@
<!DOCTYPE html>
<html>
<head>
<!-- always start with these two lines to set a clean baseline for different devices -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="style.css">
<script src="jquery.js"></script>
<title>Docs</title>
</head>
<body class="black whitet">
<style>
/*
Choose white text on a black background so you can add color in.
Pick your favorite font and choose a font size.
*/
@import url('https://fonts.googleapis.com/css?family=Oxygen');
html, body {
font-family: "Oxygen", sans-serif;
}
[contenteditable]:focus {
outline: none;
}
.meta-on, div:hover, ul:hover, ol:hover, li:hover, p:hover, span:hover, form:hover, button:hover, input:hover, textarea:hover, img:hover {
outline: 1px solid;
animation: meta-on 3s infinite;
transition: none !important;
} @keyframes meta-on {
0% {outline-color: magenta;}
33% {outline-color: cyan;}
66% {outline-color: yellow;}
100% {outline-color: magenta;}
}
</style>
<div class="hold full hue2">
<div id="page" class="max focus gap" style="margin-top: 9%;"></div>
</div>
<script src="../../gun/gun.js"></script>
<script src="../../gun/lib/monotype.js"></script>
<script src="../../gun/lib/meta.js"></script>
<script src="../../gun/lib/normalize.js"></script>
<script async src="../../gun/lib/fun.js"></script>
<script async src="../../gun/lib/wave.js"></script>
<!-- script async src="https://edide.io/music.lib"></script -->
<script>
var gun = Gun();
var page = {};
//var gun = Gun(['https://guntest.herokuapp.com/gun', 'http://localhost:8765/gun']);
;(window.onhashchange = function(){
var file = (location.hash||'').slice(1);
var S = +new Date;
$('#page').empty().attr('contenteditable', 'false');
gun.get('test/gun/docs/'+file).get('what').map().on(function render(data, i, msg, eve){
var tmp = page[i] || '';
var last = Gun.state.is(gun._.root.graph[msg.put['#']], i);
if(last < tmp.last){ return }
//});
//if(window.LOCK){ return }
var p = $('#page').children().get(i);
if(!p){
$('#page').append('<p>');
setTimeout(function(){ render(data, i, msg, eve) },0);
return;
}
var DBG = {s: +new Date};
var r = monotype(p);
DBG.mono = +new Date;
var safe = $.normalize(data);
DBG.norm = +new Date;
p.outerHTML = data;
DBG.html = +new Date;
r.restore();
DBG.rest = +new Date;
//console.log("mono:", DBG.mono - DBG.s, "norm:", DBG.norm - DBG.mono, 'html:', DBG.html - DBG.norm, 'rest:', DBG.rest - DBG.html, ':::', msg, eve);
});
})();
window.requestAnimationFrame = window.requestAnimationFrame || setTimeout;
window.requestAnimationFrame(function frame(){
window.requestAnimationFrame(frame, 16);
}, 16);
document.execCommand('defaultParagraphSeparator', false, 'p');
meta.edit({
name: "Edit",
combo: ['E'],
use: function(eve){
console.log('on');
}, on: function(eve){
if($(eve.target).closest('p').length){ return }
var edit = this;
setTimeout(function(){ meta.flip(false) },1);
edit.init();
$(document).on('keydown.tmp', '[contenteditable]', function(eve){
if(eve.which != 13){ return }
eve.preventDefault();
var r = window.getSelection().getRangeAt(0);
var c = r.commonAncestorContainer, p;
r.deleteContents();
var p = c.splitText? $(c.splitText(r.startOffset)).parent() : $(c);
var n = $("<"+p.get(0).tagName+">"), f;
p.contents().each(function(){
if(this === c){ return f = true }
if(!f){ return }
n.append(this);
});
p.after(n);
edit.select(n.get(0));
// make sure we re-save & sync each changed paragraph.
edit.save(p);
p.nextAll().each(function(){
edit.save(this);
});
}).on('keyup.tmp', '[contenteditable]', function(eve){
//$('#debug').val(doc.html());
var p = $(window.getSelection().anchorNode).closest('p'), tmp;
(tmp = page[p.index()] || (page[p.index()] = {})).last = (+new Date) + 99;
clearTimeout(tmp.to); tmp.to = setTimeout(function(){
var DBG = {s: +new Date};
var r = monotype(p);
DBG.m = +new Date;
var html = p.html() || '';
DBG.g = +new Date;
if(!html && !p.prev().length && !p.next().length && !$('#page').html()){
edit.init();
}
DBG.i = +new Date;
var safe = $.normalize(html);
DBG.n = +new Date;
p.html(safe);
DBG.h = +new Date;
r.restore();
DBG.r = +new Date;
edit.save(p);
DBG.p = +new Date;
//console.log("save:", DBG.p - DBG.r, "rest:", DBG.r - DBG.h, "html:", DBG.h - DBG.n, "norm:", DBG.n - DBG.i, 'init:', DBG.i - DBG.g, 'grab:', DBG.g - DBG.m, 'mono:', DBG.m - DBG.s);
},50)});
},
up: function(){
console.log("UP");
$('[contenteditable=true]').off('.tmp');
},
init: function(){
var edit = this;
var doc = $('#page').attr('contenteditable', 'true');
if(!doc.text()){
doc.html('<p class="loud crack"></p>');
}
edit.select(doc.children().first().get(0));
},
save: function(p){
p = $(p);
var i = p.index();// = Array.prototype.indexOf.call(parent.children, child);
var file = (location.hash||'').slice(1);
var data = (p.get(0)||{}).outerHTML||'';
//data = $.normalize(data); // GOOD TO DO SECURITY ON SENDING SIDE TOO!!!
window.LOCK = true;
gun.get('test/gun/docs/'+file).get('what').get(i).put(data);
window.LOCK = false;
},
select: function(p){
var s = window.getSelection(),
r = document.createRange();
if(p.innerHTML){
r.setStart(p, 0);
r.collapse(true);
s.removeAllRanges();
s.addRange(r);
return;
}
p.innerHTML = '\u00a0';
r.selectNodeContents(p);
s.removeAllRanges();
s.addRange(r);
document.execCommand('delete', false, null);
}
});
;(function(){
meta.edit({name: "Design", combo: ['D']});
meta.edit({name: "Fill", combo: ['D','F'], // TODO!
use: function(eve){},
on: function(eve){
var on = meta.tap();
meta.ask('Color name, code, or URL?', function(color){
on.css('background', color);
}, true);
},
up: function(eve){}
});
meta.edit({name: "Add", combo: ['D','A']});
meta.edit({name: "Row", combo: ['D','A', 'R'],
on: function(eve){
meta.tap().append('<div style="min-height: 9em; padding: 2%;">');
}
});
meta.edit({name: "Columns", combo: ['D','A','C'],
on: function(eve){
var on = meta.tap().addClass('center'), tmp, c;
var html = '<div class="unit col" style="min-height: 9em; padding: 2%;"></div>';
if(!on.children('.col').length){ html += html }
c = (tmp = on.append(html).children('.col')).length;
tmp.each(function(){
$(this).css('width', (100/c)+'%');
})
}
});
meta.edit({name: "Text", combo: ['D','A','T'],
on: function(eve){
var tag = $('<p>text</p>');
meta.tap().append(tag);
tag.focus();
}
});
meta.edit({name: "Delete", combo: ['D','A','D'],
on: function(eve){
meta.tap().remove();
}
});
meta.edit({name: "Turn", combo: ['D','T']});
meta.edit({name: "Size", combo: ['D','S']});
meta.edit({name: "X", combo: ['D','S','X'],
on: function(eve){
var on = this.a = meta.tap().addClass('meta-on'), was = on.width();
$(document).on('mousemove.tmp', function(eve){
var be = was + ((eve.pageX||0) - was);
on.css({'max-width': be, width: '100%'});
});
meta.ask('Width in px, %, or other unit?', function(w){
if(!w){ return }
on.css({'max-width': w, width: '100%'});
}, true);
}, up: function(){
$(document).off('mousemove.tmp');
this.a.removeClass('meta-on');
}
});
meta.edit({name: "Y", combo: ['D','S','Y'],
//on: function(eve){ console.log('on Y') },
on: function(eve){ console.log('use Y')
var on = this.a = meta.tap().addClass('meta-on'), was = on.height();
$(document).on('mousemove.tmp', function(eve){
var be = was + ((eve.pageY||0) - was);
on.css({'min-height': be});
})
}, up: function(){ console.log('up Y')
$(document).off('mousemove.tmp');
this.a.removeClass('meta-on');
}
});
}());
;(function(){
var logic = {};
meta.edit({name: "Logic", combo: ['L']});
meta.edit({name: "Symbol", combo: ['L','S'],
on: function(eve){
console.log(1);
}
});
meta.edit({name: "Action", combo: ['L','A'],
on: function(eve){
console.log(2);
}
});
meta.edit({name: "Data", combo: ['L','D'],
on: function(eve){
console.log(3);
}
});
}());
;(function(){
var song = {};
// TODO:
// 1. Manually OR automatically load music.js API, dependencies, and modules. - FINE for now
// 2. only export music API, not meta, not dom, not mouselock system, not UI/html, etc. better module isolation and export.
// 3. `var wave = Wave('a').play()` // also on `Music.now`
// defaults... instrument: pure tones, volume curve: |\_ , speed curve: 0.5
// 4. `wave.blur(0.5).itch(0.5);`
// 5. wave.long(2); // how long in seconds each note plays, optionally: wave.pace(60) is bpm
// 6. wave.loud(0.5); // 0% to 100% volume loudness of device output.
// 7. wave.vary(0.5); // slows down or speeds up wiggle per harmonic
// 8:
// wave structure, does ToneJS allow us to change the sine wave smoothness/type continuously or is it a pre-fixed type?
// wave structure: /\/\/, |_|, /|/, \|\| do some research with ToneJS whether these are dynamic or fixed
// wave.itch(); // changes the shape of the wiggle from smooth sine to square or triangle
// wave.blur(220hz); // blur may not apply/work on pure notes other than filtering them.
meta.edit({name: "Music", combo: ['M']});
meta.edit({name: "Play", combo: ['M','P'],
on: function(eve){
// TODO: We still need to add to meta API ability to change name.
if(song.play){
music.stop();
song.play = false;
return;
}
song.play = true;
music.stop();
setTimeout(function(){
song.now = wave($('#page').text()).play();
},250);
}
});
meta.edit({name: "Blur", combo: ['M','B'],
on: function(eve){
$(document).on('mousemove.tmp', function(eve){
var x = eve.pageX;
song.now.loud(x/$('body').innerWidth());
});
},
up: function(){
$(document).off('.tmp');
}
});
$(document).on('keydown', function(eve){
if(eve.which === music.which){ return }
music.play(String.fromCharCode(music.which = eve.which));
});
}());
;(function(){
/*
Edit
Bold
Italic
Link
?
Left
Middle
Right
Justify
?
Small
Normal
Header
Title
Design
Add
Row
Column
Text
Delete
Turn
Grab
Size
X
Y
Fill
Logic
Symbol
Action
Data
*/
/*
*/
}());
</script>
</body>
</html>

439
examples/game/furball.html Normal file
View File

@ -0,0 +1,439 @@
<!DOCTYPE html>
<html>
<head>
<!-- always start with these two lines to set a clean baseline for different devices -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="../style.css">
<!-- link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/gun/examples/style.css" -->
<script src="https://cdn.jsdelivr.net/npm/gun/examples/jquery.js"></script>
<title>Furball</title>
</head>
<body class="black whitet">
<style>
/*
Choose white text on a black background so you can add color in.
Pick your favorite font and choose a font size.
*/
@import url('https://fonts.googleapis.com/css?family=Mali');
html, body {
font-family: "Mali", sans-serif;
}
.huef {
background: #4D79D8;
-webkit-animation: huef 9s infinite;
animation: huef 9s infinite;
} @keyframes huef {
0% {background-color: #4D79D8;}
25% {background-color: #33cc33;}
50% {background-color: #f2b919;}
75% {background-color: #ea3224;}
100% {background-color: #4D79D8;}
} @-webkit-keyframes huef {
0% {background-color: #4D79D8;}
25% {background-color: #33cc33;}
50% {background-color: #f2b919;}
75% {background-color: #ea3224;}
100% {background-color: #4D79D8;}
}
button, input {
padding: 1em;
background: transparent;
border: 1px solid white;
border-radius: 1.5em;
color: white;
margin: 0.5em;
margin-bottom: 0;
cursor: pointer;
}
button:hover, input:hover {
background: white;
color: black;
transform: scale(1.1);
}
.air { padding-top: 9%; }
.yak button { font-size: 80%; }
.wag {
-webkit-animation: wag 3s infinite;
animation: wag 3s infinite;
} @keyframes wag {
0% {transform: rotate(0deg);}
50% {transform: rotate(-1deg);}
100% {transform: rotate(0deg);}
}
@keyframes print {
0% { overflow: hidden; height: 0vh; }
99% { overflow: hidden; height: 100vh; }
100% { overflow: visible; height: auto; }
}
input {
outline: none;
}
</style>
</style>
<!-- for educational sites, consider starting with a nice full screen welcome message -->
<div class="home hold full huef center air">
<div class="focus row">
<p><i>Neon ERA presents</i></p>
<p class="shout wag">Furball Forces</p>
<!-- just like in real life, say who you are and give a concise reason why you add value to someone's life and then make a call to action, if they want to learn more they can always scroll to learn more -->
<div>
<!-- a class="unit hold" href="#fullscreen"><button>WATCH TRAILER</button></a -->
<a class="unit yak" href="#choose"><button>PLAY GAME</button></a>
</div>
</div>
<div class="focus center row leak">
<!-- just like in real life, looking pretty attracts attention, so show off and look glamorous! -->
<img class="unit blink" src="file:///Users/mark/Pictures/supercatdog.png" style="min-width: 10em; width: 80%;">
</div>
<script>location.hash = ''</script>
<script src="https://cdn.jsdelivr.net/npm/gun/lib/fun.js"></script>
<script>;(function(){
// OPTIONAL MUSIC:
$('.home button').on('click', function(){
if(window.screen.height > window.screen.width){ return }
$('body').append("<div id='audio' onclick='$(this).remove();'><iframe width='0' height='0' src='https://www.youtube-nocookie.com/embed/LLPoZGX0qZk?autoplay=1' frameborder='0'></iframe></div>");
})
}());
</script>
<style>#audio { padding: 0.5em; position: fixed; bottom: 0; left: 0; } #audio:before { content: '\25BA'; } #audio:hover:before { content: '\25FC'; }</style>
</div>
<div id="choose" class="hold full hue4 center air">
<div class="focus row">
<p class="shout wag fur">Choose Team:</p>
<div>
<a class="unit yak" href="#automecha"><button style="background: white; color: black;">#AutoMecha</button></a>
<a class="unit yak" href="#cyberninjas"><button style="background: black; color: white; border-color: black;">#CyberNinjas</button></a>
</div>
</div>
<div class="focus center row leak">
<img class="unit blink" src="file:///Users/mark/Pictures/supercatdog.png" style="transform: scaleX(-1); filter: invert(1); min-width: 10em; width: 80%;">
</div>
</div>
<div id="cyberninjas" class="hold full black">
<style>
#cyberninjas:target .story {
animation: print 3s steps(50, end);
}
</style>
<div class="story pad">
<p class="loud crack">Episode 1: Waking</p>
<p>"How long until they're online?"</p>
<p>"We're copying the soul files, almost done."</p>
<p>"Monsters are on the bridge, we do not have time!"</p>
<p>"The new body is printing now, it'll be able to outrun them all, just hold on."</p>
<p>"It won't know where to run! We're risking ruining the whole resistance, I need to talk to it now."</p>
<p>"95% done." The voice behind the glass turns to the soul in the body, "My cub, can you hear me?"</p>
<p>...</p>
<a class="unit yak" href="#cyberninjas2"><button>Reply "Yes, Mom?"</button></a>
</div>
<script>
;(function(){
$('#cyberninjas a').on('click', function(){
$('#hud .life').removeClass('down');
});
}());
</script>
</div>
<div id="cyberninjas2" class="hold full red">
<style>
#cyberninjas2:target .story {
animation: print 3s steps(50, end);
}
#hud {
opacity: 0.4;
font-family: 'Audiowide', cursive;
z-index: 999999999999;
transition: all 3s;
}
#hud .life {
position: fixed;
left: 50%;
bottom: 0px;
padding: 0.25em 1em 0.1em;
border-radius: 0.5em 0.5em 0 0;
transform: translateX(-50%);
background: black;
text-shadow: 0em -0.125em 0.75em white;
}
#hud .score {
position: fixed;
left: 50%;
top: 0px;
padding: 0.1em 1em 0.25em;
border-radius: 0 0 0.5em 0.5em;
transform: translateX(-50%);
background: black;
text-shadow: 0em 0.1em 0.75em white;
}
#hud .down {
bottom: -2em !important;
}
#hud .up {
top: -2em !important;
}
</style>
<div id="hud">
<div class="score shade up">
SCORE: <span id="hudscore">0</span>%
</div>
<div class="life shade down">
LIFE: <span id="hudlife">50</span>%
</div>
</div>
<div class="story pad">
<p>A fire explodes in the room behind the glass as an AutoMecha blows the door open.</p>
<p>The floor shakes and the bed crashes through the wall, flying out of the building.</p>
<p class="center">"Mom!!!"</p>
<p>There is a total free fall from 10 levels up, water down below.</p>
<p>...</p>
<a class="unit yak" href="#cyberninjas3"><button>Dive or Die</button></a>
</div>
<script>
;(function(){
$('#cyberninjas2 a').on('click', function(){
$('#hudlife').text($('#hudlife').data().is = 50);
});
}());
</script>
</div>
<div id="cyberninjas3" class="hold full blue">
<style>
@import url('https://fonts.googleapis.com/css?family=Audiowide');
#cyberninjas3:target .story {
animation: print 3s steps(50, end);
}
</style>
<div class="story pad">
<p>The water splashes, swelling and swirling all around.</p>
<p>...</p>
<p class="center">Rapidly tap to swim up to air:</p>
<a class="unit yak"><button>Swim</button></a>
</div>
<script>
;(function(){
var go, life = $('#hudlife').data();
$('#cyberninjas3 a').on('click', function(){
$('#hudlife').text(life.is += 5);
if(100 <= life.is){
location.hash = 'cyberninjas4';
clearInterval(go);
go = false;
return;
}
if(go){ return }
go = setInterval(function(){
if(0 >= life.is){
location.hash = 'cyberninjas2';
$('#hudlife').text(life.is = 50);
clearInterval(go);
go = false;
return;
}
$('#hudlife').text(life.is -= 5);
}, 1000); // 1 second
});
}());
</script>
</div>
<div id="cyberninjas4" class="hold full black">
<style>
#cyberninjas4:target .story {
animation: print 3s steps(50, end);
}
</style>
<div class="story pad">
<p class="loud crack">Episode 2: Who Am I?</p>
<p>"Grab on!" A voice calls out from the darkness.</p>
<p>A life vest hits the water and floats within arm's distance.</p>
<p>The shivering body is pulled up onto the boat.</p>
<p>"Wow, you're heavier than you look. Are you OK? What's your name?"</p>
<p>...</p>
<p class="center">Write your reply & hit enter:</p>
<form class="center">
<input class="loud" style="width: 60%;">
</form>
</div>
<script>
;(function(){
$('form').on('submit', function(eve){ eve.preventDefault() });
$('#cyberninjas4').on('submit', function(){
var name = $(this).find('input').val();
if(!name.length){ return }
$('.story-name').text(' '+(window.NAME = name));
$('#hud .score').removeClass('up');
location.hash = 'cyberninjas5';
})
}());
</script>
</div>
<div id="cyberninjas5" class="hold full green">
<style>
#cyberninjas5:target .story {
animation: print 3s steps(50, end);
}
</style>
<div class="story pad">
<p>"Well<span class="story-name"></span>, it's a miracle you did not die in the building explosion or from that fall."</p>
<p>"What is going on? What happened?"</p>
<p>"You can't remember? Your brain must be knocked up pretty hard."</p>
<p>"No, I was mid copy into this body and now my memories are glitching."</p>
<p>"Woah, you're one of those pro elite AREION revolutionaries? All flesh & blood! Dense, too. I would've assumed they were stealing AutoMecha tech for that instead."</p>
<p>"I was about to be told vital data for the resistance, but then they blew up the build--"</p>
<p>...</p>
<p>"Hey, what's the matter?"</p>
<p>"My mom. She was in there. I need to go back. Please, help me and tell me everything you know."</p>
<p>"I'm so sorry. I can only get so close with the boat, you're gonna have to jump over a lot of broken bits. You ready?"</p>
<p>...</p>
<a class="unit yak" href="#cyberninjas6"><button>GO!</button></a>
</div>
</div>
<div id="cyberninjas6" class="hold full green">
<style>
#cyberninjas6:target .story {
animation: print 3s steps(50, end);
}
</style>
<div class="story pad">
<p class="center">... to be continued ...</p>
<div id="player" style="position: fixed; width: 1em; height: 1em; background: white; left: 50%; top: 50%; border-radius: 100%;"></div>
<!-- jumping game ? like offline dinosaur ? -->
</div>
<script src="../../../gun/lib/meta.js"></script>
<script>
;(function(){
var p = $('#player');
p.x = 50;
p.y = 50;
meta.edit({
name: "Up",
combo: ["W"],
on: function(){
console.log("up");
this.to = this.to || setInterval(this.on, 100);
$("html, body").stop().animate({ scrollTop: $(window).scrollTop()-100 }, 100);
p.css({top: --p.y +'%'});
},
use: function(){},
up: function(){ clearTimeout(this.to); this.to = 0 }
});
meta.edit({
name: "Left",
combo: ["A"],
on: function(){
console.log("left");
this.to = this.to || setInterval(this.on, 100);
p.css({left: --p.x +'%'});
},
use: function(){},
up: function(){ clearTimeout(this.to); this.to = 0 }
});
meta.edit({
name: "Down",
combo: ["S"],
on: function on(){
console.log("down");
this.to = this.to || setInterval(this.on, 100);
$("html, body").stop().animate({ scrollTop: $(window).scrollTop()+100 }, 100);
p.css({top: ++p.y +'%'});
},
use: function(){},
up: function(){ clearTimeout(this.to); this.to = 0 }
});
meta.edit({
name: "Right",
combo: ["D"],
on: function(){
console.log("right");
this.to = this.to || setInterval(this.on, 100);
p.css({left: ++p.x +'%'});
},
use: function(){},
up: function(){ clearTimeout(this.to); this.to = 0 }
});
meta.edit({
name: "Jump",
combo: [32],
on: function(){ console.log("jump") },
use: function(){},
up: function(){}
});
meta.edit({
name: "Crouch",
combo: [16],
on: function(){ console.log("crouch") },
use: function(){},
up: function(){}
});
meta.edit({
name: "Use",
combo: ["E"],
on: function(){ console.log("use") },
use: function(){},
up: function(){}
});
meta.edit({
name: "Fire",
combo: ["F"],
on: function(){ console.log("fire") },
use: function(){},
up: function(){}
});
meta.edit({
name: "Switch",
combo: [9],
on: function(){ console.log("Switch") },
use: function(){},
up: function(){}
});
window.requestAnimationFrame = window.requestAnimationFrame || setTimeout;
window.requestAnimationFrame(function frame(){
window.requestAnimationFrame(frame, 16);
}, 16);
}());
</script>
</div>
<div id="automecha" class="hold full white blackt">
<style>
#automecha:target .story {
animation: print 3s steps(50, end);
}
</style>
<div class="story pad">
<p class="loud crack">Episode 1: Training</p>
<p>...</p>
</div>
</div>
<div class="hold black center">
<div class="pad">
<div class="left">
<p class="loud">For <i>You</i>,</p>
<p>Crafted with love, <span class="redt"></span> by ERA.</p>
</div>
</div>
<div>
<img src="https://era.eco/media/world.png" class="row">
</div>
</div>
</body>
</html>

View File

@ -3,7 +3,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body style="text-align: center;">
<h1 id="when" style="font-size: 7vw; margin-top: 43vh;"></h1>
<h1 id="when" style="font-size: 7vw; margin-top: 43vh; font-family: monospace;"></h1>
</body>
<script src="/gun.js"></script>
<script src="/gun/nts.js"></script>
@ -16,4 +16,4 @@
when.innerHTML = print;
});
</script>
</html>
</html>

View File

@ -10,12 +10,12 @@
<p id="debug" style="position: fixed; bottom: 0px; color: white; height: 1em;"></p>
<script src="../jquery.js"></script>
<script src="../../../gun/gun.js"></script>
<script src="../../../gun/nts.js"></script>
<!-- script src="../../../gun/nts.js"></script -->
<script src="../../../gun/lib/webrtc.js"></script>
<script>
// Thanks to https://github.com/dmcinnes/HTML5-Asteroids
//var gun = Gun();
var gun = Gun(location.host? location.origin+'/gun' : 'http://localhost:8765/gun');
var gun = GUN(location.origin + '/gun');
var game = {gun: gun.get('example/game/space'), area: {}, ships: {}};
game.keys = {38: 'up', 37: 'left', 39: 'right', 40: 'down', 32: 'space'};
$(document).on('keydown', function(e){
@ -50,7 +50,7 @@
}
game.ship = function(d, el){
if(!d){ // spawn our ship
var id = Gun.text.random(1, 'abcdefghijklmno');
var id = String.random(1, 'abcdefghijklmno');
game.me = game.ships[id] = {data: d = {id: id, t: game.now}};
game.me.gun = game.gun.get('players').get(d.id).put(d);
return;
@ -101,7 +101,8 @@
s.ay = 0;
}
Gun.obj.map(game.ships, function(ship){
Object.keys(game.ships).forEach(function(key, ship){
ship = game.ships[key];
if(ship.gun){ return }
if(!ship.l){ return }
if(ship.x-50 <= s.x && s.x <= ship.x+50
@ -131,7 +132,7 @@
var d = s.data;
var dt = (now - d.t) || 0;
if(dt > 30 * 1000){
Gun.obj.del(game.ships, d.id);
delete game.ships[d.id];
s.$.remove();
return true;
}
@ -143,7 +144,7 @@
}
s.x = d.x + d.vx * dt;
s.y = d.y + d.vy * dt;
s.x = s.x % area.x;
if(s.x < 0){
s.x += area.x;
@ -159,13 +160,13 @@
}
return s;
}
localStorage.clear();
game.sync = function(shoot){
var me = game.me;
if(!me || me.boom){ return }
var keys = game.keys;
if(shoot || keys.up || keys.right || keys.left || keys.down){
var data = Gun.obj.to(me.data);
var data = {};
Object.keys(me.data).forEach(function(k){ data[k] = me.data[k] }); // 1 layer clone.
data.x = data.x / game.area.x;
data.y = data.y / game.area.y;
me.gun.put(data);
@ -182,7 +183,7 @@
game.resize();
game.ship();
game.gun.get('players').map().on(function(data, id){
data = Gun.obj.copy(data);
data = JSON.parse(JSON.stringify(data)); // clone object, this is bad perf tho.
data.x = data.x * game.area.x;
data.y = data.y * game.area.y;
data.id = data.id || id;
@ -196,7 +197,8 @@
now = game.now = Gun.state();
diff = now - last;
last = now;
Gun.obj.map(ships, function(ship){
Object.keys(ships).forEach(function(key, ship){
ship = ships[key];
if(!ship.frame){ return }
ship.frame(diff, now);
});
@ -210,7 +212,9 @@
gun.on('hi', function(peer){
console.log("hi!", peer);
if(peer.url){ return }
Gun.obj.map(gun.back('opt.peers'), function(peer){
var peers = gun.back('opt.peers');
Object.keys(peers).forEach(function(id, peer){
peer = peers[id];
if(!peer.url || !peer.wire){ return }
peer.wire._send = peer.wire.send;
peer.wire.send = send;
@ -221,7 +225,7 @@
});
function send(raw){
if(!raw){ return }
if(raw.indexOf('webrtc') >= 0){
if(raw.indexOf('rtc') >= 0){
if(!this._send){ return }
return this._send(raw);
}
@ -274,4 +278,4 @@
left: -50px;
}
</style>
</html>
</html>

331
examples/game/win.html Normal file
View File

@ -0,0 +1,331 @@
<!DOCTYPE html>
<html>
<head>
<!-- always start with these two lines to set a clean baseline for different devices -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="../style.css">
<!-- link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/gun/examples/style.css" -->
<script src="https://cdn.jsdelivr.net/npm/gun/examples/jquery.js"></script>
<title>Win</title>
</head>
<body class="black whitet">
<style>
/*
Choose white text on a black background so you can add color in.
Pick your favorite font and choose a font size.
*/
@import url('https://fonts.googleapis.com/css?family=Montserrat');
html, body {
font-family: "Montserrat", sans-serif;
}
button, input {
padding: 1em;
background: transparent;
border: 1px solid white;
border-radius: 1.5em;
color: white;
margin: 0.5em;
margin-bottom: 0;
cursor: pointer;
}
button:hover, input:hover {
background: white;
color: black;
transform: scale(1.1);
}
.air { padding-top: 9%; }
.yak button { font-size: 80%; }
.wag {
-webkit-animation: wag 3s infinite;
animation: wag 3s infinite;
} @keyframes wag {
0% {transform: rotate(0deg);}
50% {transform: rotate(-1deg);}
100% {transform: rotate(0deg);}
}
</style>
</style>
<!-- for educational sites, consider starting with a nice full screen welcome message -->
<div class="home hold full huef center air">
<div class="focus row">
<p><i>how to</i></p>
<p class="shout wag">Win at Life!</p>
<p><i>success, fame, power.</i></p>
<p><i>sex, ethics, & integrity.</i></p>
<!-- just like in real life, say who you are and give a concise reason why you add value to someone's life and then make a call to action, if they want to learn more they can always scroll to learn more -->
<div>
<!-- a class="unit hold" href="#fullscreen"><button>WATCH TRAILER</button></a -->
<a class="unit yak gap" href="#breathe"><button>PLAY GAME</button></a>
</div>
</div>
<div class="focus center row leak">
<!-- just like in real life, looking pretty attracts attention, so show off and look glamorous! -->
<img class="unit blink" src="" style="min-width: 10em; width: 80%;">
</div>
<script>location.hash = ''</script>
<script src="https://cdn.jsdelivr.net/npm/gun/lib/fun.js"></script>
</div>
<div id="breathe" class="hold full green">
<style>
</style>
<div class="story pad">
<p class="loud crack">Step 1: Breathe</p>
<a class="unit yak" href="#water"><button>Yupe</button></a>
</div>
<script>
</script>
</div>
<div id="water" class="hold full blue">
<style>
</style>
<div class="story pad">
<p class="loud crack">Step 2: Drink Water</p>
<p></p>
<a class="unit yak" href="#eat"><button>Next</button></a>
</div>
<script>
</script>
</div>
<div id="eat" class="hold full red">
<style>
</style>
<div class="story pad">
<p class="loud crack">Step 3: Eat Once a Day</p>
<p>If you do not want to be eaten, do not eat things that would not want to be eaten.</p>
<a class="unit yak" href="#babies"><button>Got It</button></a>
</div>
<script>
</script>
</div>
<div id="babies" class="hold full red">
<style>
</style>
<div class="story pad">
<p class="loud crack">Step 4: Babymaking* 😉</p>
<p>Find a willing player.</p>
<p><small> * This does not always make babies.</small></p>
<a class="unit yak" href="#make"><button>How?</button></a>
</div>
<script>
</script>
</div>
<div id="make" class="hold full hue">
<style>
</style>
<div class="story pad">
<p class="loud crack">Step 5: Make Dance</p>
<p>Moving your body is how you express your thoughts.</p>
<p>What you do with your body is what others will come to know you for. So do well.</p>
<p>You can make art, songs, or stories; You can make science, tools, or discoveries.</p>
<p>Who are you?</p>
<a class="unit yak" href="#science"><button>I am a Scientist!</button></a>
<a class="unit yak" href="#art"><button>I am an Artist!</button></a>
</div>
<script>
</script>
</div>
<div id="science" class="hold full hue">
<div class="story pad">
<p class="loud crack">Science: Knowing Games</p>
<p>If you must win one game, it should be the game of making games.</p>
<p>If you can make any game, then you will know how to win any game.</p>
<a class="unit yak" href="#games"><button>Games?</button></a>
</div>
<script>
</script>
</div>
<div id="games" class="hold full hue">
<div class="story pad">
<p>Games have goals and play.</p>
<p>Play is a safe space to try new dances.</p>
<p>Goals try to get players to do a type of dance.</p>
<a class="unit yak" href="#swim"><button>Start</button></a>
</div>
<script>
</script>
</div>
<div id="swim" class="hold full blue">
<style>
@import url('https://fonts.googleapis.com/css?family=Audiowide');
#hud {
opacity: 0.4;
font-family: 'Audiowide', cursive;
z-index: 999999999999;
transition: all 3s;
}
#hud .life {
position: fixed;
left: 50%;
bottom: 0px;
padding: 0.25em 1em 0.1em;
border-radius: 0.5em 0.5em 0 0;
transform: translateX(-50%);
background: black;
text-shadow: 0em -0.125em 0.75em white;
}
#hud .score {
position: fixed;
left: 50%;
top: 0px;
padding: 0.1em 1em 0.25em;
border-radius: 0 0 0.5em 0.5em;
transform: translateX(-50%);
background: black;
text-shadow: 0em 0.1em 0.75em white;
}
#hud .down {
bottom: -2em !important;
}
#hud .up {
top: -2em !important;
}
</style>
<div class="story pad">
<p>The simplest goal is to not "die" in the game.</p>
<p>Oh look, you've fallen into water and cannot breathe.</p>
<p>If you do not push the button to swim to the top, you'll lose the game.</p>
<a class="unit yak"><button>Swim</button></a>
</div>
<div id="hud">
<div class="score shade up">
SCORE: <span id="hudscore">0</span>%
</div>
<div class="life shade down">
LIFE: <span id="hudlife">100</span>%
</div>
</div>
<script>
;(function(){
var go, life = $('#hudlife').data();
$(window).on('hashchange', function(){
if(location.hash != '#swim'){ return }
$('#hudlife').text($('#hudlife').data().is = 100);
$('#hud .life').removeClass('down');
go = setInterval(function(){
if(0 >= life.is){
location.hash = 'die';
clearInterval(go);
go = false;
return;
}
$('#hudlife').text(life.is -= 1);
}, 100);
})
$('#swim a').on('click', function(){
$('#hudlife').text((life.is += 5) < 100? life.is : 100);
if(100 <= life.is){
location.hash = 'won';
clearInterval(go);
go = false;
return;
}
});
}());
</script>
</div>
<div id="won" class="hold full hue">
<div class="story pad">
<p>You won your first game! 🎉</p>
<p>See? I told you breathing is important.</p>
<p>You also learned the most basic dance: rapid poking.</p>
<p>The goal of the next game is to make the game you just won.</p>
<a class="unit yak" href="#won" style="z-index: 999999;"><button>Make</button></a>
</div>
<script src="https://cdn.jsdelivr.net/gh/amark/gun/lib/meta.js"></script>
<script>
meta.edit({name: "Add", combo: ['A']});
meta.edit({
name: "Timer",
combo: ['A','T'],
on: function(){
console.log("up");
this.to = this.to || setInterval(this.on, 100);
$("html, body").stop().animate({ scrollTop: $(window).scrollTop()-100 }, 100);
p.css({top: --p.y +'%'});
},
use: function(){},
up: function(){ clearTimeout(this.to); this.to = 0 }
});
meta.edit({
name: "Delete",
combo: ['A','D'],
on: function(){
$(meta.tap.on).remove();
},
use: function(){},
up: function(){}
});
meta.edit({
name: "Button",
combo: ['A','B'],
on: function(){
$(meta.tap.on).append("<a class='unit yak'><button>Button</button></a>")
},
use: function(){},
up: function(){}
});
meta.edit({
name: "Edit",
combo: ['E'],
on: function(){
$('body').attr('contenteditable', 'true' == $('body').attr('contenteditable')? false : true);
},
use: function(){},
up: function(){ }
});
</script>
<script>
window.requestAnimationFrame(function frame(){
return;
window.requestAnimationFrame(frame);
if(location.hash != '#won'){ return }
var p = $('#won a').offset();
var bx = p.left, by = p.top, mx = meta.tap.x, my = meta.tap.y;
bx = mx - bx; by = my - by;
var d = Math.sqrt(bx*bx + by*by);
console.log(bx, by, mx, my, d);
if(d > 250){ return }
$('#won a').css({position: 'fixed', left: bx + (Math.random()*100), top: by + (Math.random()*100)});
})
</script>
</div>
<div id="rules" class="hold full white blackt">
<style>
</style>
<div class="story pad">
<p class="loud crack">Bend these Rules if it is more Moral to do so</p>
<p></p>
<a class="unit yak" href="#water"><button>Next</button></a>
</div>
<script>
</script>
</div>
<div id="die" class="hold full red">
<style>
</style>
<div class="story pad">
<p class="loud crack">GAME OVER</p>
<p></p>
<a class="unit yak" href="#make"><button>Start Over</button></a>
</div>
<script>
</script>
</div>
</body>
</html>

View File

@ -1,66 +0,0 @@
var port = process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8765;
var Gun = require('../');
// have to do this before instancing gun(?)
Gun.on('out', function(msg){
this.to.next( msg );
msg = JSON.stringify(msg);
gunPeers.forEach( function(peer){ peer.send( msg ) })
})
var gun = Gun({
file: 'data.json'
});
var server = require('http').createServer(function(req, res){
var insert = "";
if( req.url.endsWith( "gun.js" ) )
insert = "../";
require('fs').createReadStream(require('path').join(__dirname, insert, req.url)).on('error',function(){ // static files!
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(require('fs').readFileSync(require('path').join(__dirname, 'index.html'))); // or default to index
}).pipe(res); // stream
});
// do not do this to attach server... instead pull websocket provider and use that.
// gun.wsp(server);
var ws = require( 'ws' ); // default websocket provider gun used...
var WebSocketServer = ws.Server;
var wss = new WebSocketServer( {
server: server, // 'ws' npm
autoAcceptConnections : false // want to handle the request (websocket npm?)
});
wss.on('connection',acceptConnection )
var gunPeers = []; // used as a list of connected clients.
function acceptConnection( connection ) {
// connection.upgradeReq.headers['sec-websocket-protocol'] === (if present) protocol requested by client
// connection.upgradeReq.url === url request
console.log( "connect?", connection.upgradeReq.headers, connection.upgradeReq.url )
gunPeers.push( connection );
connection.on( 'error',function(error){console.log( "WebSocket Error:", error) } );
connection.on('message', function (msg) {
msg = JSON.parse(msg)
if ("forEach" in msg) msg.forEach(m => gun.on('in', JSON.parse(m)));
else gun.on('in', msg)
})
connection.on( 'close', function(reason,desc){
// gunpeers gone.
var i = gunPeers.findIndex( function(p){return p===connection} );
if( i >= 0 )
gunPeers.splice( i, 1 );
})
}
server.listen(port);
console.log('Server started on port ' + port + ' with ');

View File

@ -1,21 +1,34 @@
;(function(){
var cluster = require('cluster');
if(cluster.isMaster){
return cluster.fork() && cluster.on('exit', function(){ cluster.fork() });
}
var fs = require('fs');
var config = { port: process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8765 };
var Gun = require('../'); // require('gun')
if(process.env.HTTPS_KEY){
config.key = fs.readFileSync(process.env.HTTPS_KEY);
config.cert = fs.readFileSync(process.env.HTTPS_CERT);
config.server = require('https').createServer(config, Gun.serve(__dirname));
} else {
config.server = require('http').createServer(Gun.serve(__dirname));
}
var gun = Gun({web: config.server.listen(config.port) });
console.log('Relay peer started on port ' + config.port + ' with /gun');
;(function(){
var cluster = require('cluster');
if(cluster.isMaster){
return cluster.fork() && cluster.on('exit',function(){ cluster.fork(); require('../lib/crashed') });
}
var fs = require('fs'), env = process.env;
var GUN = require('../'); // require('gun');
var opt = {
port: env.PORT || process.argv[2] || 8765,
peers: env.PEERS && env.PEERS.split(',') || []
};
if(fs.existsSync((opt.home = require('os').homedir())+'/cert.pem')){
env.HTTPS_KEY = env.HTTPS_KEY || opt.home+'/key.pem';
env.HTTPS_CERT = env.HTTPS_CERT || opt.home+'/cert.pem';
}
if(env.HTTPS_KEY){
opt.port = 443;
opt.key = fs.readFileSync(env.HTTPS_KEY);
opt.cert = fs.readFileSync(env.HTTPS_CERT);
opt.server = require('https').createServer(opt, GUN.serve(__dirname));
require('http').createServer(function(req, res){
res.writeHead(301, {"Location": "https://"+req.headers['host']+req.url });
res.end();
}).listen(80);
} else {
opt.server = require('http').createServer(GUN.serve(__dirname));
}
var gun = GUN({web: opt.server.listen(opt.port), peers: opt.peers});
console.log('Relay peer started on port ' + opt.port + ' with /gun');
module.exports = gun;
}());

8
examples/https.sh Normal file
View File

@ -0,0 +1,8 @@
#!/bin/bash
cd ~
git clone https://github.com/acmesh-official/acme.sh.git
cd ~/acme.sh
./acme.sh --install -m $EMAIL
bash ~/acme.sh/acme.sh --issue -d $DOMAIN -w $WEB
bash ~/acme.sh/acme.sh --install-cert -d $DOMAIN --key-file ~/key.pem --fullchain-file ~/cert.pem --reloadcmd "service relay force-reload"

View File

@ -1,28 +1,4 @@
<html>
<head>
<title>gun examples</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<!-- h1>Examples Directory <button style="float: right;" onclick="localStorage.clear()">Clear Local Storage</button></h1 -->
<style>
html, body {
margin: 0;
padding: 0;
}
iframe {
width: 100%;
height: 50%;
border: none;
b-order-top: ridge 2em skyblue;
border: none;
}
</style>
<a href="/todo/index.html"><iframe src="/todo/index.html"></iframe></a>
<!-- a href="/json/index.html"><iframe src="/json/index.html"></iframe></a -->
<a href="/chat/index.html"><iframe src="/chat/index.html"></iframe></a>
<!-- script src="../gun.js"></script -->
</body>
</html>
<!DOCTYPE html>
<p>This is the examples folder.
<p>The most basic example is <a href="./basic/note.html">./basic/note.html</a>!</p>
<p>Home page temporarily disabled.</p>

View File

@ -0,0 +1,116 @@
const DEFAULT_OPTIONS = {
size: 20,
stickTo: 'top',
};
class ScrollWindow {
constructor(gunNode, opts = {}) {
this.opts = Object.assign(DEFAULT_OPTIONS, opts);
this.elements = new Map();
this.node = gunNode;
this.center = this.opts.startAt;
this.updateSubscriptions();
}
updateSubscriptions() {
this.upSubscription && this.upSubscription.off();
this.downSubscription && this.downSubscription.off();
const subscribe = params => {
this.node.get({ '.': params}).map().on((val, key, a, eve) => {
if (params['-']) {
this.downSubscription = eve;
} else {
this.upSubscription = eve;
}
this._addElement(key, val);
});
};
if (this.center) {
subscribe({ '>': this.center, '<': '\uffff' });
subscribe({'<': this.center, '>' : '', '-': true});
} else {
subscribe({ '<': '\uffff', '>': '', '-': this.opts.stickTo === 'top' });
}
}
_getSortedKeys() {
this.sortedKeys = this.sortedKeys || [...this.elements.keys()].sort();
return this.sortedKeys;
}
_upOrDown(n, up) {
this.opts.stickTo = null;
const keys = this._getSortedKeys();
n = n || (keys.length / 2);
n = up ? n : -n;
const half = Math.floor(keys.length / 2);
const newMiddleIndex = Math.max(Math.min(half + n, keys.length - 1), 0);
if (this.center !== keys[newMiddleIndex]) {
this.center = keys[newMiddleIndex];
this.updateSubscriptions();
}
return this.center;
}
up(n) {
return this._upOrDown(n, true);
}
down(n) {
return this._upOrDown(n, false);
}
_topOrBottom(top) {
this.opts.stickTo = top ? 'top' : 'bottom';
this.center = null;
this.updateSubscriptions();
}
top() {
this._topOrBottom(true);
}
bottom() {
this._topOrBottom(false);
}
_addElement(key, val) {
if (!val || this.elements.has(key)) return;
const add = () => {
this.elements.set(key, val);
this.sortedKeys = [...this.elements.keys()].sort();
const sortedElements = this.sortedKeys.map(k => this.elements.get(k));
this.opts.onChange && this.opts.onChange(sortedElements);
};
const keys = this._getSortedKeys();
if (keys.length < this.opts.size) {
add();
} else {
if (this.opts.stickTo === 'top' && key > keys[0]) {
this.elements.delete(keys[0]);
add();
} else if (this.opts.stickTo === 'bottom' && key < keys[keys.length - 1]) {
this.elements.delete(keys[keys.length - 1]);
add();
} else if (this.center) {
if (keys.indexOf(this.center) < (keys.length / 2)) {
if (key < keys[keys.length - 1]) {
this.elements.delete(keys[keys.length - 1]);
add();
}
} else {
if (key > keys[0]) {
delete this.elements.delete(keys[0]);
add();
}
}
}
}
}
getElements() {
return this.elements;
}
}

View File

@ -0,0 +1,30 @@
<html>
<head>
<title>Infinite scroll example</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
<link rel="stylesheet" type="text/css" href="./style.css">
<script src="/jquery.js"></script>
<script src="/gun.js"></script>
<script src="./ScrollWindow.js"></script>
</head>
<body>
<header>
<form id="generate">
<input type="text" id="number" placeholder="Number of posts"/>
<button>Generate</button>
</form>
<div id="top-buttons">
<button id="top">Top</button>
</div>
</header>
<div id="top-sentinel" style="padding-top: 0px;"></div>
<div id="container"></div>
<div id="bottom-sentinel" style="padding-top: 0px;"></div>
<button id="bottom">Bottom</button>
<script src="./index.js"></script>
</body>
</html>

View File

@ -0,0 +1,167 @@
const gun = new Gun();
const size = 20;
const gunNode = gun.get('posts');
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
let topSentinelPreviousY = 0;
let topSentinelPreviousRatio = 0;
let bottomSentinelPreviousY = 0;
let bottomSentinelPreviousRatio = 0;
let previousUpIndex = previousDownIndex = -1;
const render = elements => {
const t = new Date();
elements.reverse().forEach((data, j) => {
var date = new Date(data.date);
$('#date' + j).text(date.toLocaleDateString() + ' ' + date.toLocaleTimeString());
$('#text' + j).text(data.text);
$('#img' + j).attr('src', '');
$('#img' + j).attr('src', _getCatImg(date.getTime()));
$('#post' + j).css({visibility: 'visible'});
});
console.log('rendering took', new Date().getTime() - t.getTime(), 'ms');
window.onRender && window.onRender(elements);
};
const onChange = debounce(render, 20);
const scroller = new ScrollWindow(gunNode, {size, stickTo: 'top', onChange});
const initList = () => {
for (var n = 0; n < size; n++) {
var el = $("<div>").addClass('post').attr('id', 'post' + n).css({visibility: 'hidden'});
el.append($('<b>').attr('id', 'date' + n));
el.append($('<span>').attr('id', 'text' + n));
el.append($('<img>').attr('id', 'img' + n).attr('height', 100).attr('width', 100));
$('#container').append(el);
}
}
const _getCatImg = (n) => {
const url = "https://source.unsplash.com/collection/139386/100x100/?sig=";
return url + n % 999999;
};
const getNumFromStyle = numStr => Number(numStr.substring(0, numStr.length - 2));
const adjustPaddings = isScrollDown => {
const container = document.getElementById("container");
const currentPaddingTop = getNumFromStyle(container.style.paddingTop);
const currentPaddingBottom = getNumFromStyle(container.style.paddingBottom);
const remPaddingsVal = 198 * (size / 2); // TODO: calculate actual element heights
if (isScrollDown) {
container.style.paddingTop = currentPaddingTop + remPaddingsVal + "px";
container.style.paddingBottom = currentPaddingBottom === 0 ? "0px" : currentPaddingBottom - remPaddingsVal + "px";
} else {
container.style.paddingBottom = currentPaddingBottom + remPaddingsVal + "px";
if (currentPaddingTop === 0) {
$(window).scrollTop($('#post0').offset().top + remPaddingsVal);
} else {
container.style.paddingTop = currentPaddingTop - remPaddingsVal + "px";
}
}
}
const topSentCallback = entry => {
const container = document.getElementById("container");
const currentY = entry.boundingClientRect.top;
const currentRatio = entry.intersectionRatio;
const isIntersecting = entry.isIntersecting;
// conditional check for Scrolling up
if (
currentY > topSentinelPreviousY &&
isIntersecting &&
currentRatio >= topSentinelPreviousRatio &&
scroller.center !== previousUpIndex && // stop if no new results were received
scroller.opts.stickTo !== 'top'
) {
previousUpIndex = scroller.center;
adjustPaddings(false);
scroller.up(size / 2);
}
topSentinelPreviousY = currentY;
topSentinelPreviousRatio = currentRatio;
}
const botSentCallback = entry => {
const currentY = entry.boundingClientRect.top;
const currentRatio = entry.intersectionRatio;
const isIntersecting = entry.isIntersecting;
// conditional check for Scrolling down
if (
currentY < bottomSentinelPreviousY &&
currentRatio > bottomSentinelPreviousRatio &&
isIntersecting &&
scroller.center !== previousDownIndex && // stop if no new results were received
scroller.opts.stickTo !== 'bottom'
) {
previousDownIndex = scroller.center;
adjustPaddings(true);
scroller.down(size / 2);
}
bottomSentinelPreviousY = currentY;
bottomSentinelPreviousRatio = currentRatio;
}
const initIntersectionObserver = () => {
const options = {
//rootMargin: '190px',
}
const callback = entries => {
entries.forEach(entry => {
if (entry.target.id === 'post0') {
topSentCallback(entry);
} else if (entry.target.id === `post${size - 1}`) {
botSentCallback(entry);
}
});
}
var observer = new IntersectionObserver(callback, options); // TODO: It's possible to quickly scroll past the sentinels without them firing. Top and bottom sentinels should extend to page top & bottom?
observer.observe(document.querySelector("#post0"));
observer.observe(document.querySelector(`#post${size - 1}`));
}
initList(size);
initIntersectionObserver();
$('#top').click(() => {
scroller.top();
$('#container').css({'padding-top': 0, 'padding-bottom': 0});
$(document.body).animate({ scrollTop: 0 }, 500);
});
$('#bottom').click(() => {
scroller.bottom();
$('#container').css({'padding-top': 0, 'padding-bottom': 0});
$(document.body).animate({ scrollTop: $("#container").height() }, 500);
});
$('#generate').submit(e => {
e.preventDefault();
const day = 24 * 60 * 60 * 1000;
const year = 365 * day;
const n = Number($('#number').val());
for (let i = 0; i < n; i++) {
const d = new Date(40 * year + i * day).toISOString();
gunNode.get(d).put({text: 'Hello world!', date: d});
}
});

View File

@ -0,0 +1,77 @@
html, body {
margin: 0;
padding: 0;
}
body {
padding-top: 65px;
}
header {
background: rgba(255,255,255,0.75);
padding: 15px;
}
header {
position: fixed;
top: 0; left: 0; right: 0;
}
#bottom {
position: fixed;
right: 15px;
bottom: 15px;
}
input {
border: 0;
background-color: #efefef;
padding: 15px;
border-radius: 50px;
}
button {
background-color: #4a4f9d;
border: 0;
padding: 15px;
color: white;
border-radius: 50px;
}
input, button {
outline: none;
opacity: 1;
}
input:focus, button:focus, button:hover {
opacity: 0.8;
}
button:hover {
cursor: pointer;
}
#top-buttons, form {
display: inline-block;
margin: 0;
}
#top-buttons {
float: right;
}
.post {
margin: 15px;
padding: 15px;
background-color: #9de1fe;
border-radius: 5px;
}
.post b {
margin-right: 5;
}
.post img {
display: block;
margin: 10px 0;
}

View File

@ -3,27 +3,51 @@
# README
# This will install nodejs and npm on your system,
# should work on most places other than Windows.
# For it to run on boot as a server, a recent OS is needed.
# Set any environment variables before you run this,
# like `export RAD=false` to disable storage, or
# pass file paths of `HTTPS_CERT` & `HTTPS_KEY`, etc.
# Copy paste and run each line into your terminal.
# If you are on Windows, http://nodejs.org/download/ has
# an installer that will automatically do it for you.
# curl -o- https://raw.githubusercontent.com/amark/gun/master/examples/install.sh | bash
# wget -O - https://raw.githubusercontent.com/amark/gun/master/examples/install.sh | bash
#debian/ubuntu
su -
cd ~
apt-get install sudo -y
sudo apt-get update -y
sudo apt-get install curl git git-core -y
sudo apt-get install curl git git-core systemd -y
sudo apt-get install systemctl -y
#fedora/openSUSE
sudo yum check-update -y
sudo yum install curl git git-core -y
sudo yum install curl git git-core systemd -y
sudo yum install systemctl -y
#screen -S install # You can safely CTRL+A+D to escape without stopping the process. `screen -R install` to resume. Stop all with `killall screen`. Note: May need to `sudo apt-get install screen`
# install nodejs
git clone http://github.com/isaacs/nave.git
sudo ./nave/nave.sh usemain stable
git clone https://github.com/isaacs/nave.git
./nave/nave.sh usemain stable
# If you just want nodejs and npm but not gun, stop here.
npm install gun
# to run the gun examples:
cd ./node_modules/gun
#npm install gun@latest
#cd ./node_modules/gun
mkdir node_modules
git clone https://github.com/amark/gun.git
cd gun
git checkout .
git pull
git checkout master
git checkout $VERSION
git pull
npm install .
sudo /usr/local/bin/node ./examples/http.js 80 # change `80` to `8765` for development purposes.
cp ./examples/relay.service /lib/systemd/system/relay.service
echo $PWD >> /lib/systemd/system/relay.service
echo "fs.file-max = 999999" >> /etc/sysctl.conf
ulimit -u unlimited
sysctl -p /etc/sysctl.conf
systemctl daemon-reload
systemctl enable relay
systemctl restart relay

1
examples/iris Symbolic link
View File

@ -0,0 +1 @@
../node_modules/iris-messenger/src

View File

@ -58,7 +58,7 @@
color: skyblue;
background: transparent;
text-decoration: none;
cursor: poiner;
cursor: pointer;
}
ul, li {
list-style-type: none;

View File

@ -178,21 +178,21 @@
});
$('#share').addClass("hide");
} else {
document.cookie = 'gps=' + (gps.track = (document.cookie.match(/gps\=(.*?)(\&|$|\;)/i)||[])[1] || Gun.text.random(5)); // trick with cookies!
document.cookie = 'gps=' + (gps.track = (document.cookie.match(/gps\=(.*?)(\&|$|\;)/i)||[])[1] || String.random(5)); // trick with cookies!
gps.ref = gun.get('gps/' + gps.track);
gps.opt.track = function(pos){
pos = pos.latlng;
if(gps.follow
|| Gun.time.is() - gps.when < 1000
|| Gun.state() - gps.when < 1000
|| gps.last && gps.last.lat == pos.lat && gps.last.lng == pos.lng){
return; // throttle!
}
gps.when = Gun.time.is();
gps.when = Gun.state();
gps.ref.put(gps.last = pos);
//$('#debug').value = JSON.stringify(gps.last);
}
gps.where = gps.where || Where(gps.opt);
$('#follow').text(("where.gunDB.io/" || (location.origin + location.pathname)) + '#' + gps.track);
$('#follow').text((location.origin + location.pathname) + '#' + gps.track);
$('#share').removeClass("hide");
$('#share').on('click', function(){
$('#link').toggleClass("hide");

View File

@ -10,7 +10,7 @@ import { fromObjects, toObjects } from './asyncSerialize';
import { subtle } from './compat';
export function parse(text) {
return __awaiter(this, void 0, void 0, function* () {
// need decodeURIComponent so binary strings are transfered properly
// need decodeURIComponent so binary strings are transferred properly
const deocodedText = unescape(text);
const objects = JSON.parse(deocodedText);
return fromObjects(serializers(true), objects);
@ -19,7 +19,7 @@ export function parse(text) {
export function stringify(value, waitForArrayBufferView = true) {
return __awaiter(this, void 0, void 0, function* () {
const serialized = yield toObjects(serializers(waitForArrayBufferView), value);
// need encodeURIComponent so binary strings are transfered properly
// need encodeURIComponent so binary strings are transferred properly
const message = JSON.stringify(serialized);
return escape(message);
});
@ -147,7 +147,7 @@ const CryptoKeySerializer = {
};
}),
fromObject: (cks) => __awaiter(this, void 0, void 0, function* () {
// if we don't have access to to a real crypto implementation, just return
// if we don't have access to a real crypto implementation, just return
// the serialized crypto key
if (crypto.fake) {
const newCks = Object.assign({}, cks);

View File

@ -1,18 +0,0 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env
npm-debug.log*
yarn-debug.log*
yarn-error.log*

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +0,0 @@
{
"name": "gun-react-examples",
"version": "1.1.0",
"private": true,
"dependencies": {
"gun": "file:../..",
"react": "^15.5.4",
"react-dom": "^15.5.4"
},
"devDependencies": {
"express": "^4.15.2",
"express-http-proxy": "^0.11.0",
"react-scripts": "0.9.5"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"server": "PORT=8081 node ./server.js"
}
}

View File

@ -1,21 +0,0 @@
console.log("If modules not found, run `npm install` in /example folder!");
var port = process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8765;
var host = process.env.OPENSHIFT_NODEJS_HOST || process.env.VCAP_APP_HOST || process.env.HOST || 'localhost';
var express = require('express');
var proxy = require('express-http-proxy');
var http = require('http');
var app = express();
var server = http.createServer(app);
var Gun = require('gun');
var gun = Gun({
file: 'data.json',
web: server
});
app.use(Gun.serve);
app.use(proxy(host + ':8765'));
server.listen(port);
console.log('Server started on port ' + port + ' with /gun');

39
examples/react/todo.html Normal file
View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<body>
<div id="app"></div>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
<script type="text/babel">
const gun = Gun();
const App = () => {
const newTodo = React.useRef()
const [todos, setTodos] = React.useState({})
React.useEffect(() => {
return gun
.get("todos")
.map()
.on((todo, id) => setTodos(todos => ({...todos, [id]: todo }))).off;
}, [])
return (
<div>
<title>TODOs</title>
<ul>{Object.values(todos).map(({title}, i) => <li key={i}>{title}</li>)}</ul>
<form onSubmit={e => {
e.preventDefault();
gun.get("todos").set({ title: newTodo.current.value });
newTodo.current.value = ''
}}>
<input ref={newTodo} placeholder="new todo"/>
</form>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
</script>
</body>
</html>

View File

@ -0,0 +1,26 @@
# gun-relay-sqlite
Manager+Adapter Stable version
Support iOS & Android
# Quickly build a relay manager and Gun storage adapter with sqlite persistence.
```base
yarn install
```
```base
yarn build
```
```base
npx cap sync
```
```base
npx cap open ios | android
```
# Preview
![relayios](https://github.com/user-attachments/assets/258050f2-328c-42f8-a8b0-0ab24efa9acf)

View File

@ -0,0 +1,43 @@
import { CapacitorConfig } from '@capacitor/cli';
import { CapacitorHttp } from '@capacitor/core';
import { KeyboardResize, KeyboardStyle } from '@capacitor/keyboard'
const config: CapacitorConfig = {
appId: 'com.gun.relay',
appName: 'Relay',
webDir: 'dist',
plugins: {
CapacitorHttp: {
enabled: true,
},
Keyboard: {
resize: KeyboardResize.None,
resizeOnFullScreen: true,
},
CapacitorSQLite: {
migrate: true,
iosDatabaseLocation: 'Library/CapacitorDatabase',
iosIsEncryption: true,
iosKeychainPrefix: 'gundb',
iosBiometric: {
biometricAuth: false,
biometricTitle : "Biometric login for capacitor sqlite"
},
androidIsEncryption: true,
androidBiometric: {
biometricAuth : false,
biometricTitle : "Biometric login for capacitor sqlite",
biometricSubTitle : "Log in using your biometric"
},
electronIsEncryption: true,
electronWindowsLocation: "C:\\ProgramData\\CapacitorDatabases",
electronMacLocation: "~/Databases/",
electronLinuxLocation: "Databases"
}
}
};
export default config;

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Gun-Relay-sqlite</title>
<base href="/" />
<meta name="color-scheme" content="light dark" />
<meta
name="viewport"
content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<link rel="shortcut icon" type="image/png" href="/favicon.png" />
<!-- add to homescreen for ios -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-title" content="Gun-Relay-sqlite" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@ -0,0 +1,204 @@
{
"name": "gun-relay-sqlite",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "npm run copy:sql:wasm && vite --host",
"build:web": "npm run copy:sql:wasm && npm run build",
"build:native": "npm run remove:sql:wasm && npm run build",
"build": "vue-tsc && vite build",
"preview": "vite preview",
"ionic:serve:before": "npm run copy:sql:wasm",
"copy:sql:wasm": "copyfiles -u 3 node_modules/sql.js/dist/sql-wasm.wasm public/assets",
"remove:sql:wasm": "rimraf public/assets/sql-wasm.wasm",
"ios:start": "npm run remove:sql:wasm && npm run build:native && npx cap sync && npx cap copy && npx cap open ios",
"android:start": "npm run remove:sql:wasm && npm run build:native && npx cap sync && npx cap copy && npx cap open android",
"electron:install": "cd electron && npm install && cd ..",
"electron:prepare": "npm run remove:sql:wasm && npm run build && npx cap sync @capacitor-community/electron && npx cap copy @capacitor-community/electron",
"electron:start": "npm run electron:prepare && cd electron && npm run electron:start",
"clean:vite:cache": "vite clean",
"test:e2e": "cypress run",
"test:unit": "vitest",
"lint": "eslint"
},
"dependencies": {
"@angular/core": "^19.1.6",
"@aparajita/capacitor-biometric-auth": "^9.0.0",
"@aparajita/capacitor-secure-storage": "^6.0.1",
"@capacitor-community/barcode-scanner": "^4.0.1",
"@capacitor-community/electron": "^4.1.2",
"@capacitor-community/media": "^8.0.0",
"@capacitor-community/speech-recognition": "^6.0.1",
"@capacitor-community/sqlite": "^5.2.3",
"@capacitor/action-sheet": "^7.0.0",
"@capacitor/android": "^7.0.0",
"@capacitor/app": "^7.0.0",
"@capacitor/assets": "latest",
"@capacitor/background-runner": "^2.1.0",
"@capacitor/browser": "^7.0.0",
"@capacitor/camera": "^7.0.0",
"@capacitor/clipboard": "^7.0.0",
"@capacitor/core": "^7.0.0",
"@capacitor/device": "^7.0.0",
"@capacitor/dialog": "^7.0.0",
"@capacitor/filesystem": "^7.0.0",
"@capacitor/geolocation": "^7.0.0",
"@capacitor/haptics": "^7.0.0",
"@capacitor/ios": "^7.0.0",
"@capacitor/keyboard": "^7.0.0",
"@capacitor/local-notifications": "^7.0.0",
"@capacitor/network": "^7.0.0",
"@capacitor/preferences": "^7.0.0",
"@capacitor/push-notifications": "^7.0.0",
"@capacitor/status-bar": "^7.0.0",
"@capacitor/toast": "^7.0.0",
"@ffmpeg/core": "^0.12.10",
"@ffmpeg/ffmpeg": "^0.12.15",
"@ffmpeg/util": "^0.12.2",
"@gun-vue/composables": "^0.24.2",
"@gun-vue/gun-es": "^0.3.1240",
"@iconify/tailwind": "latest",
"@ionic-native/background-mode": "^5.36.0",
"@ionic-native/core": "^5.36.0",
"@ionic-native/fingerprint-aio": "^5.36.0",
"@ionic-native/media": "^5.36.0",
"@ionic-native/qr-scanner": "^5.36.0",
"@ionic-native/secure-storage": "^5.36.0",
"@ionic-native/sqlite": "^5.36.0",
"@ionic/cli": "latest",
"@ionic/pwa-elements": "^3.2.2",
"@ionic/storage": "^4.0.0",
"@ionic/storage-angular": "^4.0.0",
"@ionic/vue": "^7.0.0",
"@ionic/vue-router": "^7.0.0",
"@joyid/capacitor-native-passkey": "^0.0.3",
"@peculiar/webcrypto": "^1.5.0",
"@rollup/plugin-inject": "^5.0.5",
"@scure/bip39": "^1.6.0",
"@simplewebauthn/browser": "^13.1.0",
"@tresjs/core": "latest",
"@tweenjs/tween.js": "latest",
"@types/gun": "^0.9.6",
"@types/text-encoding": "^0.0.40",
"@types/uuid": "^10.0.0",
"@types/webrtc": "^0.0.44",
"@vue/language-plugin-pug": "^2.2.2",
"@vueuse/core": "latest",
"@vueuse/gesture": "^2.0.0",
"add": "^2.0.6",
"animate.css": "latest",
"autopass": "^2.1.0",
"axios": "latest",
"b4a": "^1.6.7",
"better-scroll": "^2.5.1",
"btoa": "^1.2.1",
"buffer": "^6.0.3",
"capacitor-voice-recorder": "^7.0.5",
"cordova-plugin-background-mode": "^0.7.3",
"cordova-plugin-device": "^3.0.0",
"cordova-plugin-fingerprint-aio": "^6.0.0",
"cordova-plugin-secure-storage-echo": "^5.1.1",
"cordova-sqlite-storage": "^7.0.0",
"corestore": "^7.1.0",
"crypto-browserify": "^3.12.1",
"crypto-js": "^4.2.0",
"cryptojs": "^2.5.3",
"elliptic": "^6.6.1",
"ffmpeg": "^0.0.4",
"gsap": "^3.13.0",
"gun": "^0.2020.1240",
"gun-avatar": "^2.2.2",
"gun-flint": "^0.0.28",
"ionicons": "^7.4.0",
"jsqr": "latest",
"localforage-cordovasqlitedriver": "^1.8.0",
"lodash": "^4.17.21",
"native-run": "^2.0.1",
"node-forge": "^1.3.1",
"nvm": "latest",
"peerjs": "^1.5.4",
"photoswipe": "^5.4.4",
"pinia": "^3.0.1",
"pinyin-pro": "^3.26.0",
"postcss": "latest",
"postcss-loader": "latest",
"pug": "^3.0.3",
"qrcode.vue": "latest",
"qrcodejs2": "latest",
"sass": "latest",
"socket.io-client": "latest",
"stats.js": "latest",
"stream-browserify": "^3.0.0",
"swiper": "^11.2.6",
"tailwind": "latest",
"text-encoding": "^0.7.0",
"three": "latest",
"three-stdlib": "^2.35.16",
"tweakpane": "latest",
"uint8arrays": "latest",
"unplugin-auto-import": "^19.0.0",
"uuid": "^11.1.0",
"vite-plugin-pwa": "latest",
"vm-browserify": "^1.1.2",
"vue": "latest",
"vue-chartjs": "latest",
"vue-count-to": "^1.0.13",
"vue-i18n": "latest",
"vue-image-lightbox": "^7.2.0",
"vue-lazyload": "^3.0.0",
"vue-router": "^4.1.6",
"vue-virtual-scroller": "^2.0.0-beta.8",
"vue3-carousel": "^0.15.0",
"vue3-photo-preview": "^0.3.0",
"vue3-qrcode-reader": "latest",
"vue3-touch-events": "latest",
"webrtc-adapter": "^9.0.3"
},
"devDependencies": {
"@capacitor/cli": "^7.0.0",
"@iconify-json/tabler": "latest",
"@iconify/json": "latest",
"@tsconfig/node22": "latest",
"@types/jsdom": "latest",
"@types/node": "^22.10.7",
"@types/stats.js": "latest",
"@types/three": "latest",
"@unocss/preset-icons": "^65.4.2",
"@unocss/preset-uno": "^65.4.2",
"@vitejs/plugin-legacy": "^4.0.2",
"@vitejs/plugin-vue": "^4.0.0",
"@vitest/eslint-plugin": "latest",
"@vue/eslint-config-prettier": "latest",
"@vue/eslint-config-typescript": "^11.0.2",
"@vue/test-utils": "^2.3.0",
"@vue/tsconfig": "latest",
"copyfiles": "^2.4.1",
"cypress": "^13.1.0",
"eslint": "^8.35.0",
"eslint-plugin-oxlint": "latest",
"eslint-plugin-vue": "^9.9.0",
"jsdom": "^22.1.0",
"npm-run-all2": "latest",
"oxlint": "latest",
"prettier": "latest",
"process": "^0.11.10",
"rimraf": "^5.0.1",
"tailwindcss": "latest",
"terser": "^5.37.0",
"typescript": "^5.1.6",
"unocss": "^65.4.2",
"unplugin-vue-components": "latest",
"vite": "^latest",
"vite-plugin-node-polyfills": "^0.23.0",
"vite-plugin-pages": "latest",
"vite-plugin-vue-devtools": "latest",
"vitest": "latest",
"vue-tsc": "latest"
},
"description": "gun-sqlite-relay",
"main": "index.js",
"keywords": [],
"author": "",
"license": "ISC"
}

View File

@ -0,0 +1,131 @@
<template>
<ion-app>
<ion-router-outlet />
</ion-app>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue';
import { IonApp, IonRouterOutlet } from '@ionic/vue';
function setupNetworkListener() {
let debounceTimer: NodeJS.Timeout;
window.addEventListener('online', async () => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(async () => {
}, 500);
});
}
// console.warn
const originalConsoleWarn = console.warn;
function filterGunWarnings(...args: any[]) {
const message = args[0]?.toString() || '';
if (message.includes('Deprecated internal utility will break in next version')) {
return; // Gun.js
}
originalConsoleWarn.apply(console, args);
}
onMounted(async () => {
console.warn = filterGunWarnings;
await setupNetworkListener();
});
onUnmounted(() => {
window.removeEventListener('online', () => {});
window.removeEventListener('offline', () => {});
});
</script>
<style scoped>
/* 滚动条样式 */
::-webkit-scrollbar {
width: 0px; /* 滚动条宽度 */
background-color: transparent; /* 透明背景 */
}
/* 滚动条滑块 */
::-webkit-scrollbar-thumb {
background-color: transparent; /* 半透明的滑块颜色 */
border-radius: 4px; /* 圆角 */
}
/* 滚动条轨道 */
::-webkit-scrollbar-track {
background-color: transparent; /* 透明轨道 */
}
.ion-page,
ion-content {
background: transparent !important;
--background: transparent !important;
}
html,
body,
#app {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
/* touch-action: none;
touch-action: pan-x pan-y;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none; */
background-color: transparent;
overflow: hidden;
}
body {
font-family:
Inter,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
Oxygen,
Ubuntu,
Cantarell,
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
sans-serif;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
</style>

View File

@ -0,0 +1,638 @@
<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue';
import { useNetworkStatus } from '@/composables/useNetworkStatus';
import {
IonIcon,
IonToggle,
IonModal,
IonHeader,
IonToolbar,
IonTitle,
IonContent,
IonItem,
IonLabel,
IonInput,
IonButton,
IonButtons
} from '@ionic/vue';
import { addCircleSharp, closeCircleSharp, searchSharp, closeSharp, refreshOutline } from 'ionicons/icons';
import StorageService from '@/services/storageService';
import { getCurrentInstance } from 'vue';
const appInstance = getCurrentInstance();
const storageServ = appInstance?.appContext.config.globalProperties.$storageServ as StorageService;
const {
networkStatus,
peersStatus,
currentMode,
peerStatuses,
peersList,
enabledPeer,
addPeer,
removePeer,
enablePeer,
disablePeer,
peersNotes,
savePeerNote,
} = useNetworkStatus(storageServ);
const newPeerUrl = ref('');
const searchQuery = ref('');
const selectedPeer = ref<string | null>(null);
const isModalOpen = ref(false);
const notes = ref<Record<string, string>>({});
const isResetting = ref(false);
onMounted(async () => {
try {
const savedNotes = localStorage.getItem('relayNotes');
if (savedNotes) {
const notes = JSON.parse(savedNotes);
for (const [peer, note] of Object.entries(notes)) {
await savePeerNote(peer as string, note as string);
}
localStorage.removeItem('relayNotes'); //
console.log('Migrated relay notes to SQLite');
}
} catch (e) {
console.error('Failed to migrate relay notes:', e);
}
});
watch(peersNotes, async (newNotes) => {
for (const [peer, note] of Object.entries(newNotes)) {
await savePeerNote(peer, note);
}
}, { deep: true });
const sortedPeers = computed(() => {
return peersList.value
.slice()
.sort((a, b) => {
if (a === enabledPeer.value) return -1;
if (b === enabledPeer.value) return 1;
return 0;
})
.filter((peer) => {
const searchLower = searchQuery.value.toLowerCase();
const peerLower = peer.toLowerCase();
const noteLower = (peersNotes.value[peer] || '').toLowerCase();
return peerLower.includes(searchLower) || noteLower.includes(searchLower);
});
});
const openModal = (peer: string) => {
selectedPeer.value = peer;
isModalOpen.value = true;
};
const closeModal = () => {
isModalOpen.value = false;
selectedPeer.value = null;
};
</script>
<template>
<div class="liquid-container" >
<div class="status-bar" :class="['indicator', networkStatus]">
<div >
</div>
</div>
<div class="add-peer">
<input
v-model="newPeerUrl"
placeholder="Enter relay URL"
@keyup.enter="addPeer(newPeerUrl)"
/>
<ion-icon
:icon="addCircleSharp"
class="addlink"
@click="addPeer(newPeerUrl);newPeerUrl = ''"
/>
</div>
<div class="peer-list">
<div class="peer-list-header">
<h3>Gun Relays</h3>
<div class="search-container">
<ion-icon :icon="searchSharp" class="search-icon" />
<input
v-model="searchQuery"
placeholder="Search relays..."
class="search-input"
/>
</div>
</div>
<div class="peer-scroll-container">
<div
v-for="peer in sortedPeers"
:key="peer"
class="peer-item"
@click="openModal(peer)"
>
<div class="peer-header">
<div :class="['status', peerStatuses[peer]]">
{{ peerStatuses[peer] || 'Checking' }}
</div>
</div>
<div class="peer-content">
<span class="peer-url">{{ notes[peer] || peer }}</span>
</div>
<div class="peer-actions">
<ion-toggle
@click.stop
:checked="enabledPeer === peer"
@ionChange="enabledPeer === peer ? disablePeer() : enablePeer(peer)"
color="primary"
/>
</div>
</div>
</div>
</div>
<ion-modal :is-open="isModalOpen" @didDismiss="closeModal">
<ion-content class="ion-padding">
<ion-item lines="none" v-if="selectedPeer">
<ion-label position="stacked">Relay URL</ion-label>
<ion-input :value="selectedPeer" readonly class="readonly-input" />
</ion-item>
<ion-item lines="none" v-if="selectedPeer">
<ion-label position="stacked">Status</ion-label>
<div :class="['status', peerStatuses[selectedPeer]]" class="status-display">
{{ peerStatuses[selectedPeer] || 'Checking' }}
</div>
</ion-item>
<ion-item lines="none" v-if="selectedPeer">
<ion-label position="stacked">Note</ion-label>
<ion-input
v-model="notes[selectedPeer]"
placeholder=""
clear-input
style=" --padding-start: 12px;
--padding-end: 12px;"
/>
</ion-item>
<div class="modal-actions">
<ion-button
expand="block"
color="danger"
@click="removePeer(selectedPeer!); closeModal()"
>
Remove Relay
</ion-button>
<ion-button
expand="block"
color="medium"
@click="closeModal"
>
Close
</ion-button>
</div>
</ion-content>
</ion-modal>
</div>
</template>
<style scoped>
.liquid-container {
padding: 16px 20px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', sans-serif;
position: relative;
overflow: hidden;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background: transparent;
/* background: linear-gradient(135deg, rgba(255, 255, 255, 0.05), rgba(0, 0, 0, 0.05)); */
}
.status-bar {
display: flex;
gap: 12px;
margin-bottom: 16px;
padding: 6px 8px;
background: rgba(130, 130, 130, 0.15);
border-radius: 12px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
flex-wrap: wrap;
align-items: center;
}
.status-item {
display: flex;
align-items: center;
padding: 4px 8px;
transition: transform 0.2s ease;
}
.status-item:hover {
transform: translateY(-1px);
}
.reset-button-container {
margin-left: auto;
}
.reset-button {
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, rgba(102, 204, 255, 0.2), rgba(102, 204, 255, 0.4));
border: none;
border-radius: 8px;
padding: 6px 12px;
cursor: pointer;
transition: all 0.2s ease;
gap: 6px;
color: inherit;
font-weight: 500;
font-size: 13px;
}
.reset-button:hover {
background: linear-gradient(135deg, rgba(102, 204, 255, 0.3), rgba(102, 204, 255, 0.5));
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.reset-button:active {
transform: translateY(1px);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
}
.reset-button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.reset-icon {
font-size: 16px;
}
.rotating {
animation: spin 1.5s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.label {
font-weight: 600;
font-size: 13px;
margin-right: 6px;
/* color: #333; */
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
}
.indicator {
width: 100%;
/* padding: 4px 8px; */
border-radius: 8px;
font-size: 12px;
font-weight: 500;
backdrop-filter: blur(4px);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
}
.indicator.online,
.indicator.connected,
.indicator.relay {
background: linear-gradient(135deg, #88ff88, #55ccaa);
color: #2a2a2a;
}
.indicator.offline,
.indicator.disconnected,
.indicator.direct {
background: linear-gradient(135deg, #ff7777, #cc5555);
color: #fff;
}
.add-peer {
display: flex;
gap: 12px;
margin-bottom: 20px;
}
.add-peer input {
flex: 1;
padding: 10px 12px;
border: none;
border-radius: 12px;
background: rgba(150, 150, 150, 0.2);
font-size: 14px;
/* color: #333; */
transition: all 0.2s ease;
}
.add-peer input:hover,
.add-peer input:focus {
background: rgba(150, 150, 150, 0.25);
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15);
transform: translateY(-1px);
outline: none;
}
.addlink {
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
/* color: #66ccff; */
cursor: pointer;
transition: all 0.2s ease;
}
.addlink:hover {
/* color: #88ddff; */
transform: scale(1.1);
}
.peer-list {
flex: 1;
display: flex;
flex-direction: column;
}
.peer-list-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.peer-list h3 {
font-size: 18px;
font-weight: 600;
margin: 0;
/* color: #333; */
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
.search-container {
position: relative;
width: 220px;
}
.search-icon {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
color: #777;
font-size: 16px;
}
.search-input {
width: 100%;
padding: 8px 12px 8px 36px;
border: none;
border-radius: 12px;
background: rgba(150, 150, 150, 0.2);
font-size: 14px;
transition: all 0.2s ease;
}
.search-input:hover,
.search-input:focus {
background: rgba(150, 150, 150, 0.25);
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15);
transform: translateY(-1px);
outline: none;
}
.peer-scroll-container {
border-radius: 12px;
max-height: 539px;
overflow-y: auto;
flex: 1;
}
.peer-scroll-container::-webkit-scrollbar {
width: 6px;
}
.peer-scroll-container::-webkit-scrollbar-track {
background: rgba(150, 150, 150, 0.2);
border-radius: 6px;
}
.peer-scroll-container::-webkit-scrollbar-thumb {
background: linear-gradient(180deg, #66ccff, #88ddff);
border-radius: 6px;
}
.peer-scroll-container::-webkit-scrollbar-thumb:hover {
background: linear-gradient(180deg, #55bbff, #77ccff);
}
.peer-item {
padding: 12px;
background: linear-gradient(135deg, rgba(150, 150, 150, 0.15), rgba(255, 255, 255, 0.05));
border-radius: 12px;
margin-bottom: 12px;
transition: all 0.2s ease;
cursor: pointer;
}
.peer-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.peer-header {
display: flex;
align-items: center;
margin-bottom: 8px;
}
.status {
padding: 4px 10px;
border-radius: 8px;
font-size: 12px;
font-weight: 500;
text-align: center;
backdrop-filter: blur(4px);
text-transform: capitalize;
}
.status.connected {
background: linear-gradient(135deg, #88ff88, #55ccaa);
color: #2a2a2a;
}
.status.disconnected {
background: linear-gradient(135deg, #ff7777, #cc5555);
color: #fff;
}
.status.checking {
background: linear-gradient(135deg, #ffcc66, #ffaa33);
color: #333;
}
.peer-content {
margin-bottom: 10px;
}
.peer-url {
display: block;
word-break: break-all;
font-size: 13px;
line-height: 1.5;
padding: 6px 10px;
background: rgba(150, 150, 150, 0.1);
border-radius: 8px;
/* color: #444; */
}
.peer-actions {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
}
ion-toggle {
/* --background: rgba(150, 150, 150, 0.2);
--background-checked: linear-gradient(135deg, #66ccff, #88ddff);
--handle-background: #fff;
--handle-background-checked: #fff; */
width: 48px;
height: 24px;
/* --handle-width: 20px;
--handle-height: 20px; */
}
/* Modal Styles */
ion-modal {
--border-radius: 12px;
--max-width: 400px;
--max-height: 46%;
--backdrop-opacity: 0.4;
}
/* ion-header {
--background: rgba(150, 150, 150, 0.1);
} */
ion-toolbar {
--background: transparent;
--border-width: 0;
}
ion-title {
font-size: 18px;
font-weight: 600;
color: #333;
padding: 0 12px;
}
ion-buttons {
margin-right: 8px;
}
ion-button {
--background-activated: transparent;
}
ion-button ion-icon {
font-size: 24px;
color: #666;
}
ion-button:hover ion-icon {
color: #333;
}
.ion-padding {
padding: 16px;
}
ion-item {
--background: transparent;
--padding-start: 0;
--inner-padding-end: 0;
/* margin-bottom: 16px; */
}
ion-label {
/* color: #333 !important; */
font-weight: 500;
margin-bottom: 4px;
}
ion-input {
--background: rgba(150, 150, 150, 0.15);
--padding-start: 12px;
--padding-end: 12px;
--padding-top: 8px;
--padding-bottom: 8px;
border-radius: 8px;
font-size: 14px;
}
.readonly-input {
--padding-start: 12px;
--padding-end: 12px;
--color: #666;
--background: rgba(150, 150, 150, 0.1);
}
.status-display {
width: 100%;
padding: 8px 12px;
border-radius: 8px;
font-size: 14px;
text-align: center;
}
.modal-actions {
display: flex;
gap: 12px;
margin-top: 20px;
}
.modal-actions ion-button {
--border-radius: 8px;
--padding-start: 16px;
--padding-end: 16px;
flex: 1;
}
.modal-actions ion-button[color="danger"] {
--background: #ff6666;
--background-hover: #ff8888;
--background-activated: #ff5555;
}
.modal-actions ion-button[color="medium"] {
--background: #ccc;
--background-hover: #ddd;
--background-activated: #bbb;
}
</style>

View File

@ -0,0 +1,202 @@
import { ref, Ref } from 'vue';
import { Flint, NodeAdapter } from 'gun-flint';
import StorageService from '../services/storageService';
import { ISQLiteService } from '../services/sqliteService';
import { IDbVersionService } from '../services/dbVersionService';
// 日志工具
// const log = {
// debug: (msg: string, ...args: any[]) => console.debug(`[Gun-SQLite-Adapter] ${msg}`, ...args),
// info: (msg: string, ...args: any[]) => console.info(`[Gun-SQLite-Adapter] ${msg}`, ...args),
// warn: (msg: string, ...args: any[]) => console.warn(`[Gun-SQLite-Adapter] ${msg}`, ...args),
// error: (msg: string, ...args: any[]) => console.error(`[Gun-SQLite-Adapter] ${msg}`, ...args),
// };
// 请求队列管理,防止重复查询
class RequestQueue {
private queue: Map<string, { resolve: (data: any) => void; reject: (err: any) => void }[]> = new Map();
private debounceTimers: Map<string, NodeJS.Timeout> = new Map();
private storageServ: StorageService;
constructor(storageServ: StorageService) {
this.storageServ = storageServ;
}
async get(key: string): Promise<any> {
return new Promise((resolve, reject) => {
const handlers = this.queue.get(key) || [];
handlers.push({ resolve, reject });
this.queue.set(key, handlers);
if (!this.debounceTimers.has(key)) {
const timer = setTimeout(async () => {
const handlers = this.queue.get(key) || [];
this.queue.delete(key);
this.debounceTimers.delete(key);
try {
if (!this.storageServ.db) throw new Error('Database connection not available');
const result = await this.storageServ.query('SELECT value FROM gun_nodes WHERE key = ?', [key]);
const data = result.values && result.values.length > 0 ? JSON.parse(result.values[0].value) : null;
handlers.forEach(h => h.resolve(data));
} catch (err) {
//log.error(`Failed to get key=${key}:`, err);
handlers.forEach(h => h.reject(err));
}
}, 50);
this.debounceTimers.set(key, timer);
}
});
}
async put(soul: string, node: any): Promise<void> {
await this.storageServ.run(
'INSERT OR REPLACE INTO gun_nodes (key, value, timestamp) VALUES (?, ?, ?)',
[soul, JSON.stringify(node), Date.now()]
);
}
async batchPut(nodes: Record<string, any>): Promise<void> {
const updates: [string, string, number][] = [];
for (const soul in nodes) {
updates.push([soul, JSON.stringify(nodes[soul]), Date.now()]);
}
await this.storageServ.run(
'INSERT OR REPLACE INTO gun_nodes (key, value, timestamp) VALUES ' + updates.map(() => '(?, ?, ?)').join(','),
updates.flat()
);
}
}
// 定义适配器接口
interface GunAdapter {
opt?: (context: any, options: any) => void;
get: (key: string, done: (err: Error | null, node: any) => void) => void;
put: (node: any, done: (err: Error | null) => void) => void;
}
export interface IGunSQLiteAdapter {
initialize(): Promise<void>;
getAdapter(): GunAdapter;
isReady: Ref<boolean>;
}
// 单例实例
let instance: IGunSQLiteAdapter | null = null;
export function useGunSQLiteAdapter(
sqliteService: ISQLiteService,
dbVersionService: IDbVersionService,
storageService: StorageService
): IGunSQLiteAdapter {
if (instance) return instance;
const isReady: Ref<boolean> = ref(false);
const storageServ = storageService;
let queue: RequestQueue | null = null;
async function initialize() {
if (isReady.value) return;
try {
//log.info('Initializing Gun SQLite adapter...');
// 检查 StorageService 是否已初始化数据库
if (!storageServ.db) {
//log.warn('StorageService database not initialized, initializing now...');
const dbName = 'talkflowdb';
const loadToVersion = storageServ.loadToVersion || 2;
storageServ.db = await sqliteService.openDatabase(dbName, loadToVersion, false);
} else {
const isOpen = await storageServ.db.isDBOpen();
if (!isOpen) {
//log.warn('Database not open, reopening...');
await storageServ.db.open();
}
}
// 创建 gun_nodes 表
const result = await storageServ.execute(`
CREATE TABLE IF NOT EXISTS gun_nodes (
key TEXT PRIMARY KEY NOT NULL,
value TEXT NOT NULL,
timestamp INTEGER DEFAULT (strftime('%s', 'now'))
)
`);
if (result.changes && result.changes.changes >= 0) {
//log.info('gun_nodes table created or already exists');
} else {
throw new Error('Failed to create gun_nodes table: no changes returned');
}
queue = new RequestQueue(storageServ);
isReady.value = true;
//log.info('Gun SQLite adapter initialized successfully');
} catch (err) {
//log.error('Failed to initialize Gun SQLite adapter:', err);
throw err;
}
}
const adapterCore = {
storageServ,
queue,
opt: async function (context: any, options: any) {
// log.info('Adapter opt called:', { context, options });
await initialize();
return options;
},
get: async function (key: string, done: (err: Error | null, node: any) => void) {
try {
if (!isReady.value) await initialize();
if (!queue) throw new Error('Adapter not initialized');
const data = await queue.get(key);
done(null, data);
} catch (err) {
//log.error(`Get error for key=${key}:`, err);
done(err instanceof Error ? err : new Error('Unknown error'), null);
}
},
put: async function (node: any, done: (err: Error | null) => void) {
try {
if (!isReady.value) await initialize();
if (!queue) throw new Error('Adapter not initialized');
if (typeof node !== 'object' || node === null) throw new Error('Invalid node');
const souls = Object.keys(node).length > 1 ? Object.keys(node) : [node._?.['#'] || node._.id];
if (!souls[0]) throw new Error('Missing soul in node');
if (souls.length > 1) {
await queue.batchPut(node);
} else {
await queue.put(souls[0], node[souls[0]] || node);
}
done(null);
} catch (err) {
//log.error('Put error:', err);
done(err instanceof Error ? err : new Error('Unknown error'));
}
},
};
const adapter = new NodeAdapter(adapterCore);
Flint.register(adapter);
const gunSQLiteAdapter: IGunSQLiteAdapter = {
initialize,
getAdapter: () => adapter,
isReady,
};
instance = gunSQLiteAdapter;
return gunSQLiteAdapter;
}
export function getGunSQLiteAdapter(
sqliteService: ISQLiteService,
dbVersionService: IDbVersionService,
storageService: StorageService
): IGunSQLiteAdapter {
if (!instance) {
instance = useGunSQLiteAdapter(sqliteService, dbVersionService, storageService);
}
return instance;
}
export default getGunSQLiteAdapter;

View File

@ -0,0 +1,121 @@
import { ref } from 'vue';
import Gun, { IGunInstance } from 'gun';
// 模块级别的单例状态
const isOnline = ref(navigator.onLine);
const peersConnected = ref(false);
const checkInterval = 60000; // 每 60 秒检查一次
// 单例初始化标志和变量
let initialized = false;
let intervalId: number | null = null;
let currentGunInstance: IGunInstance<any> | null = null;
let instance: ReturnType<typeof createNetwork> | null = null;
function createNetwork(gunInstance: IGunInstance<any>) {
// 检查 Gun.js 对等节点,反复尝试直到成功
async function checkPeers(): Promise<boolean> {
const maxAttempts = 3;
let attempt = 0;
const retryDelay = 1000; // 每次尝试间隔 1 秒
while (attempt < maxAttempts) {
attempt++;
const result = await new Promise<boolean>((resolve) => {
let alive = false;
const off = gunInstance.get('~public').once((data) => {
alive = true;
off.off();
resolve(true);
});
setTimeout(() => {
if (!alive) {
resolve(false);
}
}, 5000); // 10 秒超时
});
if (result) {
peersConnected.value = true;
return true;
} else {
await new Promise(resolve => setTimeout(resolve, retryDelay));
}
}
return false; // 理论上不会到达这里,因为 maxAttempts 是 Infinity
}
async function updateNetworkStatus() {
isOnline.value = navigator.onLine;
peersConnected.value = await checkPeers();
}
function handleOnline() {
updateNetworkStatus();
}
function handleOffline() {
isOnline.value = false;
peersConnected.value = false;
}
function startChecking() {
updateNetworkStatus();
intervalId = window.setInterval(updateNetworkStatus, checkInterval);
}
function stopChecking() {
if (intervalId !== null) {
clearInterval(intervalId);
intervalId = null;
}
}
// 单例初始化逻辑
if (!initialized || currentGunInstance !== gunInstance) {
if (initialized && currentGunInstance !== gunInstance) {
// 如果 Gun 实例变了,清理旧的事件监听器和定时器
stopChecking();
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
}
initialized = true;
currentGunInstance = gunInstance;
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
startChecking();
}
return {
isOnline,
peersConnected,
updateNetworkStatus,
checkPeers,
};
}
// 导出单例
export function useNetwork(gunInstance: IGunInstance<any>) {
if (!instance || currentGunInstance !== gunInstance) {
instance = createNetwork(gunInstance);
}
return instance;
}
// 清理函数(可选,用于测试或应用卸载时)
export function cleanupNetwork() {
if (instance) {
window.removeEventListener('online', instance.updateNetworkStatus);
window.removeEventListener('offline', instance.updateNetworkStatus);
if (intervalId !== null) {
clearInterval(intervalId);
intervalId = null;
}
instance = null;
initialized = false;
currentGunInstance = null;
}
}

View File

@ -0,0 +1,558 @@
import { ref, watch } from 'vue';
import { useToast } from '@/composables/useToast';
import { useNetwork } from '@/composables/useNetwork';
import StorageService from '@/services/storageService';
import Gun from 'gun';
import 'gun/sea';
// 模块级别的单例状态
const networkStatus = ref<'online' | 'offline'>('online');
const peersStatus = ref<'connected' | 'disconnected'>('disconnected');
const currentMode = ref<'direct' | 'relay'>('direct');
const peerStatuses = ref<Record<string, 'connected' | 'disconnected'>>({});
// 单例初始化标志
let initialized = false;
let instance: ReturnType<typeof createNetworkStatus> | null = null;
function createNetworkStatus(storageService: StorageService) {
const { showToast } = useToast();
const { isOnline, peersConnected, updateNetworkStatus, checkPeers } = useNetwork(gun);
const peersNotes = ref<Record<string, string>>({});
const peersList = ref<string[]>([
'https://peer.wallie.io/gun',
'https://gun.defucc.me/gun',
'https://talkflow.team/gun',
'https://gun-manhattan.herokuapp.com/gun',
'https://gundb-relay-mlccl.ondigitalocean.app/gun',
]);
const enabledPeer = ref<string>(peersList.value[0]);
let gun = Gun({
peers: [ enabledPeer.value],
radisk: true,
localStorage: false,
gunSQLiteAdapter: {
key: 'gundb',
},
});
// 确保 storageService 已初始化
async function ensureStorageReady() {
try {
if (!storageService.db || !(await storageService.db.isDBOpen())) {
console.log('[useNetworkStatus] Initializing StorageService database...');
await storageService.initializeDatabase();
if (!storageService.db) {
throw new Error('StorageService 初始化后仍无数据库连接');
}
console.log('[useNetworkStatus] StorageService database initialized');
}
} catch (err) {
console.error('[useNetworkStatus] Failed to initialize StorageService:', err);
showToast('数据库初始化失败,请重试', 'error');
throw err;
}
}
// 保存 enabledPeer 到 SQLite
async function saveEnabledPeer() {
try {
await ensureStorageReady();
await storageService.run('UPDATE network_peers SET is_enabled = 0');
if (enabledPeer.value) {
await storageService.run(
'INSERT OR REPLACE INTO network_peers (url, is_enabled, note) VALUES (?, ?, ?)',
[enabledPeer.value, 1, peersNotes.value[enabledPeer.value] || '']
);
}
console.log(`Enabled peer saved: ${enabledPeer.value}`);
} catch (err) {
console.error('[useNetworkStatus] Failed to save enabled peer:', err);
showToast('无法保存启用节点', 'error');
}
}
// 保存节点备注
async function savePeerNote(peer: string, note: string) {
try {
await ensureStorageReady();
await storageService.run(
'UPDATE network_peers SET note = ? WHERE url = ?',
[note, peer]
);
peersNotes.value[peer] = note;
console.log(`Peer note saved: ${peer} -> ${note}`);
} catch (err) {
console.error('[useNetworkStatus] Failed to save peer note:', err);
showToast('无法保存节点备注', 'error');
}
}
// 从 SQLite 加载 Peer 配置和备注
// async function loadPeers() {
// try {
// await ensureStorageReady();
// const result = await storageService.query('SELECT url, is_enabled, note FROM network_peers');
// const peers = result.values || [];
// peersList.value = peers.map((peer: { url: string }) => peer.url);
// peersNotes.value = peers.reduce((acc: Record<string, string>, peer: { url: string; note: string }) => {
// acc[peer.url] = peer.note || '';
// return acc;
// }, {});
// const enabled = peers.find((peer: { is_enabled: number }) => peer.is_enabled === 1);
// if (enabled && peersList.value.includes(enabled.url)) {
// enabledPeer.value = enabled.url;
// } else if (peersList.value.length > 0) {
// enabledPeer.value = peersList.value[0];
// await saveEnabledPeer();
// }
// gun.opt({ peers: peersList.value });
// } catch (err) {
// console.error('[useNetworkStatus] Failed to load peers:', err);
// showToast('无法加载节点列表', 'error');
// }
// }
async function loadPeers() {
try {
await ensureStorageReady();
const result = await storageService.query('SELECT url, is_enabled, note FROM network_peers');
const peers = result.values || [];
if (peers.length === 0) {
// 如果 SQLite 表为空,插入 TalkFlowCore 的预设节点
console.log('[useNetworkStatus] network_peers 表为空,插入预设节点');
const { peersList: defaultPeersList } = getTalkFlowCore(); // 获取预设节点
for (const peerUrl of defaultPeersList.value) {
await storageService.run(
'INSERT OR IGNORE INTO network_peers (url, is_enabled, note) VALUES (?, ?, ?)',
[peerUrl, 0, '']
);
}
// 重新查询以确保数据已插入
const newResult = await storageService.query('SELECT url, is_enabled, note FROM network_peers');
peersList.value = newResult.values.map((peer: { url: string }) => peer.url);
} else {
// 使用 SQLite 中的节点
peersList.value = peers.map((peer: { url: string }) => peer.url);
}
// 加载备注
peersNotes.value = peers.reduce((acc: Record<string, string>, peer: { url: string; note: string }) => {
acc[peer.url] = peer.note || '';
return acc;
}, {});
// 设置 enabledPeer
const enabled = peers.find((peer: { is_enabled: number }) => peer.is_enabled === 1);
if (enabled && peersList.value.includes(enabled.url)) {
enabledPeer.value = enabled.url;
} else if (peersList.value.length > 0) {
enabledPeer.value = peersList.value[0];
await saveEnabledPeer();
}
// 更新 Gun 配置
gun.opt({ peers: peersList.value });
console.log('[useNetworkStatus] Gun 配置节点:', peersList.value);
} catch (err) {
console.error('[useNetworkStatus] 加载节点失败:', err);
showToast('无法加载节点列表', 'error');
}
}
// 更新网络和 Peer 状态
async function updateStatus() {
networkStatus.value = isOnline.value ? 'online' : 'offline';
peersStatus.value = peersConnected.value ? 'connected' : 'disconnected';
currentMode.value = peersConnected.value && enabledPeer.value ? 'relay' : 'direct';
await updatePeerStatuses();
}
// 检查单个 Peer 的状态(用于 UI 展示)
async function checkPeerStatus(peer: string): Promise<'connected' | 'disconnected'> {
return new Promise((resolve) => {
const tempGun = Gun({ peers: [peer] });
let connected = false;
tempGun.on('hi', () => {
connected = true;
resolve('connected');
});
setTimeout(() => {
if (!connected) resolve('disconnected');
}, 5000);
});
}
// 更新所有 Peer 的状态(用于 UI 展示)
async function updatePeerStatuses() {
for (const peer of peersList.value) {
const status = await checkPeerStatus(peer);
peerStatuses.value[peer] = status;
}
}
// 用户手动启用某个 Peer
async function enablePeer(peer: string) {
if (!peersList.value.includes(peer)) {
showToast(`节点 ${peer} 不在列表中`, 'warning');
return;
}
if (enabledPeer.value === peer) {
showToast(`${peer} 已是启用状态`, 'info');
return;
}
enabledPeer.value = peer;
await saveEnabledPeer();
gun.opt({ peers: peersList.value, priorityPeer: peer });
showToast(`启用节点: ${peer}`, 'success');
const connected = await checkPeers();
if (!connected) {
showToast(`无法连接到 ${peer},将回退到其他节点`, 'warning');
}
await updateStatus();
}
// 用户禁用当前启用的 Peer
async function disablePeer() {
if (!enabledPeer.value) {
showToast('无启用的节点', 'info');
return;
}
const oldPeer = enabledPeer.value;
enabledPeer.value = peersList.value.length > 1 ? peersList.value.find(p => p !== oldPeer) || '' : '';
await saveEnabledPeer();
gun.opt({ peers: peersList.value });
showToast(`禁用节点: ${oldPeer}`, 'success');
await updateStatus();
}
// 添加 Peer允许任意输入
async function addPeer(url: string) {
if (!url) {
showToast('请输入节点地址', 'warning');
return;
}
const trimmedUrl = url.trim();
if (peersList.value.includes(trimmedUrl)) {
showToast('该节点已存在', 'warning');
return;
}
try {
await ensureStorageReady();
console.log('[useNetworkStatus] 添加节点:', trimmedUrl);
await storageService.run(
'INSERT INTO network_peers (url, is_enabled, note) VALUES (?, ?, ?)',
[trimmedUrl, 0, '']
);
// 重新查询 network_peers 表,确保数据一致
const result = await storageService.query('SELECT url FROM network_peers');
peersList.value = result.values.map((peer: { url: string }) => peer.url);
console.log('[useNetworkStatus] 更新 peersList:', peersList.value);
gun.opt({ peers: peersList.value });
showToast(`节点已添加: ${trimmedUrl}`, 'success');
await updatePeerStatuses();
} catch (err: any) {
console.error('[useNetworkStatus] Failed to add peer:', err);
showToast(`添加节点失败: ${err.message || '未知错误'}`, 'error');
}
}
// 移除 Peer
async function removePeer(peer: string) {
try {
await ensureStorageReady();
if (enabledPeer.value === peer) {
await disablePeer();
}
await storageService.run('DELETE FROM network_peers WHERE url = ?', [peer]);
peersList.value = peersList.value.filter(p => p !== peer);
delete peerStatuses.value[peer];
gun.opt({ peers: peersList.value });
showToast(`删除节点 ${peer}`, 'success');
await updatePeerStatuses();
} catch (err) {
console.error('[useNetworkStatus] Failed to remove peer:', err);
showToast('删除节点失败', 'error');
}
}
// 处理网络状态变化
function handleOnline() {
updateNetworkStatus();
updateStatus();
}
function handleOffline() {
updateNetworkStatus();
updateStatus();
}
// 初始化逻辑,只执行一次
if (!initialized) {
initialized = true;
loadPeers();
updateStatus();
// 添加网络事件监听
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
// 监听 peersList 和 enabledPeer 的变化
watch(peersList, () => {
updatePeerStatuses();
});
watch(enabledPeer, () => {
saveEnabledPeer();
updateStatus();
});
}
return {
networkStatus,
peersStatus,
currentMode,
peerStatuses,
peersList,
enabledPeer,
addPeer,
removePeer,
enablePeer,
disablePeer,
updateStatus,
peersNotes,
savePeerNote,
};
}
// 导出单例
export function useNetworkStatus(storageService: StorageService) {
if (!instance) {
instance = createNetworkStatus(storageService);
}
return instance;
}
// import { ref, watch } from 'vue';
// import { useToast } from '@/composables/useToast';
// import { useNetwork } from '@/composables/useNetwork';
// // 模块级别的单例状态
// const networkStatus = ref<'online' | 'offline'>('online');
// const peersStatus = ref<'connected' | 'disconnected'>('disconnected');
// const currentMode = ref<'direct' | 'relay'>('direct');
// const peerStatuses = ref<Record<string, 'connected' | 'disconnected'>>({});
// // 单例初始化标志
// let initialized = false;
// let instance: ReturnType<typeof createNetworkStatus> | null = null;
// // 持久化 enabledPeer 的本地存储键
// const ENABLED_PEER_KEY = 'enabledPeer';
// function createNetworkStatus() {
// const { gun, peersList, enabledPeer } = getTalkFlowCore();
// const { showToast } = useToast();
// const { isOnline, peersConnected, updateNetworkStatus, checkPeers } = useNetwork(gun);
// // 保存 enabledPeer 到 localStorage
// function saveEnabledPeer() {
// localStorage.setItem(ENABLED_PEER_KEY, enabledPeer.value);
// }
// // 从 localStorage 加载 Peer 配置
// function loadPeers() {
// const savedPeers = localStorage.getItem('peers');
// if (savedPeers) {
// peersList.value = JSON.parse(savedPeers);
// }
// const savedPeer = localStorage.getItem(ENABLED_PEER_KEY);
// if (savedPeer && peersList.value.includes(savedPeer)) {
// enabledPeer.value = savedPeer;
// } else if (peersList.value.length > 0) {
// enabledPeer.value = peersList.value[0];
// saveEnabledPeer();
// }
// // 初始化时使用完整的 peersList
// gun.opt({ peers: peersList.value });
// }
// // 更新网络和 Peer 状态
// async function updateStatus() {
// networkStatus.value = isOnline.value ? 'online' : 'offline';
// peersStatus.value = peersConnected.value ? 'connected' : 'disconnected';
// currentMode.value = peersConnected.value && enabledPeer.value ? 'relay' : 'direct';
// await updatePeerStatuses();
// }
// // 检查单个 Peer 的状态(用于 UI 展示)
// async function checkPeerStatus(peer: string): Promise<'connected' | 'disconnected'> {
// return new Promise((resolve) => {
// const tempGun = Gun({ peers: [peer] });
// let connected = false;
// tempGun.on('hi', () => {
// connected = true;
// resolve('connected');
// });
// setTimeout(() => {
// if (!connected) resolve('disconnected');
// }, 5000);
// });
// }
// // 更新所有 Peer 的状态(用于 UI 展示)
// async function updatePeerStatuses() {
// for (const peer of peersList.value) {
// const status = await checkPeerStatus(peer);
// peerStatuses.value[peer] = status;
// }
// }
// // 用户手动启用某个 Peer
// async function enablePeer(peer: string) {
// if (!peersList.value.includes(peer)) {
// showToast(`Peer ${peer} not in list`, 'warning');
// return;
// }
// if (enabledPeer.value === peer) {
// showToast(`${peer} is already enabled`, 'info');
// return;
// }
// enabledPeer.value = peer;
// saveEnabledPeer();
// gun.opt({ peers: peersList.value, priorityPeer: peer }); // 自定义选项,优先尝试该 Peer
// showToast(`Enabled peer: ${peer}`, 'success');
// // 检查连接状态
// const connected = await checkPeers();
// if (!connected) {
// showToast(`Failed to connect to ${peer}, falling back to other peers`, 'warning');
// }
// await updateStatus();
// }
// // 用户禁用当前启用的 Peer
// function disablePeer() {
// if (!enabledPeer.value) {
// showToast('No peer enabled', 'info');
// return;
// }
// const oldPeer = enabledPeer.value;
// enabledPeer.value = peersList.value.length > 1 ? peersList.value.find(p => p !== oldPeer) || '' : '';
// saveEnabledPeer();
// gun.opt({ peers: peersList.value }); // 重置为完整列表
// showToast(`Disabled peer: ${oldPeer}`, 'success');
// updateStatus();
// }
// // 添加 Peer
// function addPeer(url: string) {
// if (!url) {
// showToast('Please enter the node URL', 'warning');
// return;
// }
// if (peersList.value.includes(url)) {
// showToast('This node already exists.', 'warning');
// return;
// }
// peersList.value.push(url);
// localStorage.setItem('peers', JSON.stringify(peersList.value));
// gun.opt({ peers: peersList.value });
// showToast(`Node added: ${url}`, 'success');
// updatePeerStatuses();
// }
// // 移除 Peer
// function removePeer(peer: string) {
// if (enabledPeer.value === peer) {
// disablePeer();
// }
// peersList.value = peersList.value.filter(p => p !== peer);
// delete peerStatuses.value[peer];
// localStorage.setItem('peers', JSON.stringify(peersList.value));
// gun.opt({ peers: peersList.value });
// showToast(`Deleted node ${peer}`, 'success');
// updatePeerStatuses();
// }
// // 处理网络状态变化
// function handleOnline() {
// updateNetworkStatus();
// updateStatus();
// }
// function handleOffline() {
// updateNetworkStatus();
// updateStatus();
// }
// // 初始化逻辑,只执行一次
// if (!initialized) {
// initialized = true;
// loadPeers();
// updateStatus();
// // 添加网络事件监听
// window.addEventListener('online', handleOnline);
// window.addEventListener('offline', handleOffline);
// // 监听 peersList 和 enabledPeer 的变化
// watch(peersList, () => {
// // gun.opt({ peers: peersList.value });
// updatePeerStatuses();
// });
// watch(enabledPeer, () => {
// saveEnabledPeer();
// updateStatus();
// });
// }
// return {
// networkStatus,
// peersStatus,
// currentMode,
// peerStatuses,
// peersList,
// enabledPeer,
// addPeer,
// removePeer,
// enablePeer,
// disablePeer,
// updateStatus,
// };
// }
// // 导出单例
// export function useNetworkStatus() {
// if (!instance) {
// instance = createNetworkStatus();
// }
// return instance;
// }
// 清理函数(用于测试或应用卸载时)
// export function cleanupNetworkStatus() {
// if (instance) {
// window.removeEventListener('online', instance.updateStatus);
// window.removeEventListener('offline', instance.updateStatus);
// instance = null;
// initialized = false;
// }
// }

View File

@ -0,0 +1,88 @@
// src/composables/useToast.ts
import { ref, onMounted } from 'vue';
import { Directory, Encoding, Filesystem } from '@capacitor/filesystem';
type ToastType = 'info' | 'success' | 'error' | 'warning';
interface ToastMessage {
id: number;
text: string;
type: ToastType;
duration: number;
}
const messages = ref<ToastMessage[]>([]);
let idCounter = 0;
const isEnabled = ref(false); // 默认开启提示
const SETTINGS_FILE = 'toast_settings.json';
async function loadSettings(): Promise<{ isToastEnabled: boolean }> {
const defaultSettings = { isToastEnabled: false };
try {
const result = await Filesystem.readFile({
path: SETTINGS_FILE,
directory: Directory.Data,
encoding: Encoding.UTF8,
});
const data = typeof result.data === 'string' ? result.data : await result.data.text();
return JSON.parse(data) || defaultSettings;
} catch (err) {
console.log('未找到提示设置文件,使用默认值');
return defaultSettings;
}
}
async function saveSettings(): Promise<void> {
try {
await Filesystem.writeFile({
path: SETTINGS_FILE,
data: JSON.stringify({ isToastEnabled: isEnabled.value }),
directory: Directory.Data,
encoding: Encoding.UTF8,
});
console.log('提示设置已保存:', { isToastEnabled: isEnabled.value });
} catch (err) {
console.error('保存提示设置失败:', err);
}
}
export function showToast(msg: string, msgType: ToastType = 'info', customDuration = 3000) {
if (!isEnabled.value) return; // 如果关闭则不显示
const toast = {
id: idCounter++,
text: msg,
type: msgType,
duration: customDuration,
};
messages.value.push(toast);
setTimeout(() => {
messages.value = messages.value.filter(m => m.id !== toast.id);
}, customDuration);
}
function hideToast(id: number) {
messages.value = messages.value.filter(m => m.id !== id);
}
function toggleToast(enabled: boolean) {
isEnabled.value = enabled;
saveSettings(); // 保存设置
}
// 初始化加载设置
onMounted(async () => {
const settings = await loadSettings();
isEnabled.value = settings.isToastEnabled;
});
export function useToast() {
return {
messages,
isEnabled,
showToast,
hideToast,
toggleToast,
};
}

View File

@ -0,0 +1,97 @@
import { createApp } from 'vue'
import App from './src/App.vue'
import router from './router';
import { IonicVue } from '@ionic/vue';
import { useRouter } from 'vue-router'
/* Core CSS required for Ionic components to work properly */
import '@ionic/vue/css/core.css';
/* Basic CSS for apps built with Ionic */
import '@ionic/vue/css/normalize.css';
import '@ionic/vue/css/structure.css';
import '@ionic/vue/css/typography.css';
import '@ionic/vue/css/ionic.bundle.css'
/* Optional CSS utils that can be commented out */
import '@ionic/vue/css/padding.css';
import '@ionic/vue/css/float-elements.css';
import '@ionic/vue/css/text-alignment.css';
import '@ionic/vue/css/text-transformation.css';
import '@ionic/vue/css/flex-utils.css';
import '@ionic/vue/css/display.css';
/* Theme variables */
import './theme/variables.css';
import { Capacitor } from '@capacitor/core';
import { JeepSqlite } from 'jeep-sqlite/dist/components/jeep-sqlite';
import { defineCustomElements as pwaElements} from '@ionic/pwa-elements/loader';
import SqliteService from './services/sqliteService';
import DbVersionService from './services/dbVersionService';
import StorageService from './services/storageService';
import InitializeAppService from './services/initializeAppService';
pwaElements(window);
customElements.define('jeep-sqlite', JeepSqlite);
const platform = Capacitor.getPlatform();
const app = createApp(App)
.use(IonicVue)
.use(useRouter)
.use(router);
// Set the platform as global properties on the app
app.config.globalProperties.$platform = platform;
// Define and instantiate the required services
const sqliteServ = new SqliteService();
const dbVersionServ = new DbVersionService();
const storageServ = new StorageService(sqliteServ, dbVersionServ);
// Set the services as global properties on the app
app.config.globalProperties.$sqliteServ = sqliteServ;
app.config.globalProperties.$dbVersionServ = dbVersionServ;
app.config.globalProperties.$storageServ = storageServ;
//Define and instantiate the InitializeAppService
const initAppServ = new InitializeAppService(sqliteServ, storageServ);
const mountApp = () => {
initAppServ.initializeApp()
.then(() => {
router.isReady().then(() => {
app.mount('#app');
});
})
.catch((error) => {
console.error('App Initialization error:', error);
});
}
if (platform !== "web") {
mountApp();
} else {
window.addEventListener('DOMContentLoaded', async () => {
const jeepEl = document.createElement("jeep-sqlite");
document.body.appendChild(jeepEl);
customElements.whenDefined('jeep-sqlite').then(() => {
mountApp();
})
.catch ((err) => {
console.error('jeep-sqlite creation error:', err);
});
});
}

View File

@ -0,0 +1,313 @@
<script setup lang="ts">
import { ref } from 'vue';
import {
IonPage, IonHeader, IonToolbar, IonButtons, IonBackButton, IonTitle, IonContent, IonIcon,
IonModal, IonToggle
} from '@ionic/vue';
import { addCircleSharp, closeCircleSharp, helpCircleOutline, closeOutline } from 'ionicons/icons';
import RelayMode from '@/components/RelayMode.vue';
const showHelpModal = ref(false);
</script>
<template>
<ion-page>
<ion-header :translucent="true" collapse="fade">
<ion-toolbar class="liquid-toolbar">
<ion-buttons slot="start">
<ion-back-button text="Discover" color="dark"></ion-back-button>
</ion-buttons>
<ion-title>Network Status</ion-title>
<ion-buttons slot="end">
<ion-button color="dark" @click="showHelpModal = true">
<ion-icon :icon="helpCircleOutline"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content :fullscreen="true" :scroll-y="false">
<RelayMode/>
</ion-content>
</ion-page>
</template>
<style scoped>
.addlink {
width: 39px;
height: 39px;
display: flex;
align-items: center;
justify-content: center;
}
/* Ionic Toolbar */
.liquid-toolbar {
--border-color: transparent;
}
/* Ionic Content */
.liquid-content {
--background: transparent;
--padding-start: 0;
--padding-end: 0;
--padding-top: 0;
--padding-bottom: 0;
}
/* Original Container Styles */
.liquid-container {
padding: 15px 20px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', sans-serif;
position: relative;
overflow: hidden;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.status-item {
display: flex;
align-items: center;
margin-bottom: 15px;
background: rgba(130, 130, 130, 0.1);
padding: 10px;
border-radius: 15px;
transition: transform 0.3s ease;
}
.status-item:hover {
transform: scale(1.02);
}
.label {
font-weight: 500;
margin-right: 10px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.indicator {
display: inline-block;
padding: 6px 12px;
border-radius: 12px;
font-size: 14px;
backdrop-filter: blur(5px);
}
.indicator.online,
.indicator.connected,
.indicator.relay {
background: linear-gradient(45deg, #99ff99, #66ffcc);
color: #333;
}
.indicator.offline,
.indicator.disconnected,
.indicator.direct {
background: linear-gradient(45deg, #ff6666, #ff9999);
}
.add-peer {
display: flex;
gap: 15px;
margin-bottom: 20px;
}
.add-peer input {
flex: 1;
padding: 10px;
border: none;
border-radius: 15px;
background: rgba(134, 134, 134, 0.25);
font-size: 14px;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.add-peer input:hover,
.add-peer input:focus {
transform: scale(1.02);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
outline: none;
}
.peer-list {
margin-top: 0;
flex: 1;
display: flex;
flex-direction: column;
}
.peer-list h3 {
font-size: 18px;
margin-bottom: 15px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.peer-scroll-container {
border-radius: 10px;
max-height: 390px;
overflow-y: auto;
flex: 1;
}
.peer-scroll-container::-webkit-scrollbar {
width: 8px;
}
.peer-scroll-container::-webkit-scrollbar-track {
background: rgba(125, 125, 125, 0.451);
border-radius: 10px;
}
.peer-scroll-container::-webkit-scrollbar-thumb {
background: linear-gradient(45deg, #66ccff, #99eeff);
border-radius: 10px;
}
.peer-scroll-container::-webkit-scrollbar-thumb:hover {
background: linear-gradient(45deg, #00b7ff, #66ddff);
}
.peer-item {
padding: 15px;
background: linear-gradient(135deg, rgba(127, 127, 127, 0.15), rgba(255, 255, 255, 0.05));
border-radius: 15px;
margin-bottom: 15px;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.peer-header {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.status {
display: inline-block;
padding: 6px 12px;
border-radius: 12px;
font-size: 12px;
text-align: center;
backdrop-filter: blur(5px);
animation: pulse 2s infinite ease-in-out;
}
.status.connected {
background: linear-gradient(45deg, #99ff99, #66ffcc);
color: #333;
}
.status.disconnected {
background: linear-gradient(45deg, #ff6666, #ff9999);
}
.status.checking {
background: linear-gradient(45deg, #ffcc66, #ffdd99);
color: #333;
}
.peer-content {
margin-bottom: 15px;
}
.peer-url {
display: block;
word-break: break-all;
font-size: 14px;
line-height: 1.4;
padding: 5px 10px;
background: rgba(124, 124, 124, 0.1);
border-radius: 10px;
}
.peer-actions {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 15px;
}
ion-toggle {
--background: rgba(128, 128, 128, 0.2);
--background-checked: #66ccff;
--handle-background: #fff;
--handle-background-checked: #fff;
width: 50px;
height: 24px;
}
.remove-icon {
color: #ff6666;
font-size: 30px;
cursor: pointer;
transition: transform 0.3s ease;
}
.remove-icon:hover {
transform: scale(1.1);
color: #ff9999;
}
.help-modal {
--border-radius: 16px;
}
.help-modal ion-toolbar {
--border-width: 0;
--background: transparent;
}
.help-content {
padding: 0 0 20px;
}
.help-content h2 {
font-size: 1.25rem;
margin: 20px 0 10px;
color: #333;
}
.help-content h3 {
font-size: 1.1rem;
margin: 15px 0 10px;
color: #333;
}
.help-content ion-list {
background: transparent;
margin-bottom: 15px;
}
.help-content ion-item {
--background: transparent;
--padding-start: 0;
--inner-padding-end: 0;
}
.help-content ion-label {
color: #666 !important;
font-size: 1rem;
}
ion-button[color="dark"] {
--background: #333;
--border-radius: 12px;
height: 44px;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
ion-button[color="dark"]:hover {
--background: #444;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}
</style>

View File

@ -0,0 +1,46 @@
import { createRouter, createWebHistory } from '@ionic/vue-router'
import { createAnimation } from '@ionic/vue'
import routes from 'virtual:generated-pages'
// import { useChatFlow } from '@/composables/TalkFlowCore'
// const { isLoggedIn } = useChatFlow()
const customRoutes = [
{
path: '/',
component: () => import('@/pages/index.vue'),
}
...routes,
]
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: customRoutes,
})
// 导航守卫
// router.beforeEach((to, from, next) => {
// // 如果用户没有登录,且访问的不是 'i18n.vue' 页面,则重定向到 'i18n.vue'
// if (!isLoggedIn.value && to.path !== '/') {
// next('/'); // 重定向到 i18n 页面
// } else {
// next(); // 允许访问目标路由
// }
// });
// router.beforeEach(async (to, from, next) => {
// const { isLoggedIn } = await import('@/composables/TalkFlowCore');
// if (!isLoggedIn.value && to.path !== '/') {
// next('/');
// } else {
// next();
// }
// });
export default router

View File

@ -0,0 +1,19 @@
export interface IDbVersionService {
setDbVersion(dbName: string, version: number): void
getDbVersion(dbName: string):number| undefined
};
class DbVersionService implements IDbVersionService {
dbNameVersionDict: Map<string, number> = new Map();
setDbVersion(dbName: string, version: number) {
this.dbNameVersionDict.set(dbName, version);
console.log(`设置数据库 ${dbName} 版本为: ${version}`);
}
getDbVersion(dbName: string): number | undefined {
const version = this.dbNameVersionDict.get(dbName);
console.log(`获取数据库 ${dbName} 版本: ${version}`);
return version;
}
}
export default DbVersionService;

View File

@ -0,0 +1,9 @@
import SQLiteService from './sqliteService';
import DbVersionService from './dbVersionService';
import StorageService from './storageService';
const sqliteServ = new SQLiteService();
const dbVerServ = new DbVersionService();
const storageServ = new StorageService(sqliteServ, dbVerServ);
export { sqliteServ, dbVerServ, storageServ };

View File

@ -0,0 +1,45 @@
import {ISQLiteService } from '../services/sqliteService';
import {IStorageService } from '../services/storageService';
export interface IInitializeAppService {
initializeApp(): Promise<boolean>
};
class InitializeAppService implements IInitializeAppService {
appInit = false;
sqliteServ!: ISQLiteService;
storageServ!: IStorageService;
platform!: string;
static platform: string;
constructor(sqliteService: ISQLiteService, storageService: IStorageService) {
this.sqliteServ = sqliteService;
this.storageServ = storageService;
this.platform = this.sqliteServ.getPlatform();
}
async initializeApp(): Promise<boolean> {
if (!this.appInit) {
try {
console.log('开始应用初始化');
if (this.platform === 'web') {
await this.sqliteServ.initWebStore();
console.log('Web 存储初始化完成');
}
await this.storageServ.initializeDatabase();
console.log('数据库初始化完成');
// if (this.platform === 'web') {
// await this.sqliteServ.saveToStore(this.storageServ.getDatabaseName());
// console.log('数据库保存到 Web 存储完成');
// }
this.appInit = true;
console.log('应用初始化成功');
} catch (error: any) {
const msg = error.message ? error.message : error;
console.error(`initializeAppError.initializeApp: ${msg}`, error);
throw new Error(`initializeAppError.initializeApp: ${msg}`);
}
}
return this.appInit;
}
}
export default InitializeAppService;

View File

@ -0,0 +1,114 @@
import { CapacitorSQLite, SQLiteConnection, SQLiteDBConnection, capSQLiteUpgradeOptions } from '@capacitor-community/sqlite';
import { Capacitor } from '@capacitor/core';
export interface ISQLiteService {
getPlatform(): string
initWebStore(): Promise<void>
addUpgradeStatement(options: capSQLiteUpgradeOptions): Promise<void>
openDatabase(dbName: string, loadToVersion: number, readOnly: boolean): Promise<SQLiteDBConnection>
closeDatabase(dbName: string, readOnly: boolean): Promise<void>
saveToStore(dbName: string): Promise<void>
saveToLocalDisk(dbName: string): Promise<void>
isConnection(dbName: string, readOnly: boolean): Promise<boolean>
};
class SQLiteService implements ISQLiteService {
platform = Capacitor.getPlatform();
sqlitePlugin = CapacitorSQLite;
sqliteConnection = new SQLiteConnection(CapacitorSQLite);
dbNameVersionDict: Map<string, number> = new Map();
getPlatform(): string {
return this.platform;
}
async initWebStore() : Promise<void> {
try {
await this.sqliteConnection.initWebStore();
} catch(error: any) {
const msg = error.message ? error.message : error;
throw new Error(`sqliteService.initWebStore: ${msg}`);
}
return;
}
async addUpgradeStatement(options: capSQLiteUpgradeOptions): Promise<void> {
try {
await this.sqlitePlugin.addUpgradeStatement(options);
} catch(error: any) {
const msg = error.message ? error.message : error;
throw new Error(`sqliteService.addUpgradeStatement: ${msg}`);
}
return;
}
async openDatabase(dbName:string, loadToVersion: number,
readOnly: boolean): Promise<SQLiteDBConnection> {
this.dbNameVersionDict.set(dbName, loadToVersion);
let encrypted = false;
const mode = encrypted ? "secret" : "no-encryption";
try {
let db: SQLiteDBConnection;
const retCC = (await this.sqliteConnection.checkConnectionsConsistency()).result;
let isConn = (await this.sqliteConnection.isConnection(dbName, readOnly)).result;
if(retCC && isConn) {
db = await this.sqliteConnection.retrieveConnection(dbName, readOnly);
} else {
db = await this.sqliteConnection
.createConnection(dbName, encrypted, mode, loadToVersion, readOnly);
}
const jeepSQlEL = document.querySelector("jeep-sqlite")
await db.open();
const res = await db.isDBOpen();
return db;
} catch(error: any) {
const msg = error.message ? error.message : error;
throw new Error(`sqliteService.openDatabase: ${msg}`);
}
}
async isConnection(dbName:string, readOnly: boolean): Promise<boolean> {
try {
const isConn = (await this.sqliteConnection.isConnection(dbName, readOnly)).result;
if (isConn != undefined) {
return isConn
} else {
throw new Error(`sqliteService.isConnection undefined`);
}
} catch(error: any) {
const msg = error.message ? error.message : error;
throw new Error(`sqliteService.isConnection: ${msg}`);
}
}
async closeDatabase(dbName:string, readOnly: boolean):Promise<void> {
try {
const isConn = (await this.sqliteConnection.isConnection(dbName, readOnly)).result;
if(isConn) {
await this.sqliteConnection.closeConnection(dbName, readOnly);
}
return;
} catch(error: any) {
const msg = error.message ? error.message : error;
throw new Error(`sqliteService.closeDatabase: ${msg}`);
}
}
async saveToStore(dbName: string): Promise<void> {
try {
await this.sqliteConnection.saveToStore(dbName);
return;
} catch(error: any) {
const msg = error.message ? error.message : error;
throw new Error(`sqliteService.saveToStore: ${msg}`);
}
}
async saveToLocalDisk(dbName: string): Promise<void> {
try {
await this.sqliteConnection.saveToLocalDisk(dbName);
return;
} catch(err:any) {
const msg = err.message ? err.message : err;
throw new Error(`sqliteService.saveToLocalDisk: ${msg}`);
}
}
}
export default SQLiteService;

View File

@ -0,0 +1,153 @@
import { BehaviorSubject } from 'rxjs';
import { CapacitorSQLite, SQLiteDBConnection } from '@capacitor-community/sqlite';
import { getCurrentInstance } from 'vue';
import { ISQLiteService } from './sqliteService';
import { IDbVersionService } from './dbVersionService';
export interface IStorageService {
initializeDatabase(): Promise<void>;
query(sql: string, params?: any[]): Promise<any>;
run(sql: string, params?: any[]): Promise<any>;
execute(sql: string): Promise<any>;
}
class StorageService implements IStorageService {
versionUpgrades = [
{
toVersion: 1,
statements: [
`CREATE TABLE IF NOT EXISTS network_peers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
url TEXT UNIQUE,
is_enabled INTEGER DEFAULT 0,
note TEXT DEFAULT ''
);`,
`CREATE TABLE IF NOT EXISTS gun_nodes (
key TEXT PRIMARY KEY NOT NULL,
value TEXT NOT NULL,
timestamp INTEGER DEFAULT (strftime('%s', 'now'))
);`,
],
},
];
loadToVersion = 1;
momentsVersion = 2;
db!: SQLiteDBConnection;
database: string = 'gundb';
sqliteServ!: ISQLiteService;
dbVerServ!: IDbVersionService;
isInitCompleted = new BehaviorSubject(false);
appInstance = getCurrentInstance();
platform!: string;
private isInitialized = false;
constructor(sqliteService: ISQLiteService, dbVersionService: IDbVersionService) {
this.sqliteServ = sqliteService;
this.dbVerServ = dbVersionService;
this.platform = this.appInstance?.appContext.config.globalProperties.$platform || 'web';
}
async initializeDatabase(): Promise<void> {
if (this.isInitialized) {
console.log('数据库已初始化,跳过重复调用');
return;
}
try {
console.log('开始初始化数据库:', this.database);
await this.sqliteServ.addUpgradeStatement({
database: this.database,
upgrade: this.versionUpgrades,
});
console.log('核心升级语句已添加');
this.db = await this.sqliteServ.openDatabase(this.database, this.loadToVersion, false);
console.log('数据库已打开,目标版本:', this.loadToVersion);
const currentVersionResult = await this.db.getVersion();
const currentVersion: number = currentVersionResult.version ?? 0;
console.log('当前数据库版本:', currentVersion);
for (const upgrade of this.versionUpgrades) {
console.log(`执行核心升级到版本 ${upgrade.toVersion}`);
for (const stmt of upgrade.statements) {
try {
await this.db.execute(stmt);
console.log('执行语句成功:', stmt);
} catch (err) {
console.error('执行语句失败:', stmt, err);
throw err;
}
}
}
this.dbVerServ.setDbVersion(this.database, this.loadToVersion);
console.log('核心数据库版本已设置为:', this.loadToVersion);
if (this.platform === 'web') {
try {
await this.sqliteServ.saveToStore(this.database);
console.log('数据库已保存到 Web 存储');
} catch (err) {
console.warn('Web 存储保存失败(非致命错误):', err);
}
}
const tablesAfter = await this.db.query("SELECT name FROM sqlite_master WHERE type='table'");
console.log('初始化后的表:', tablesAfter.values);
this.isInitCompleted.next(true);
this.isInitialized = true;
console.log('SQLite 数据库初始化成功');
} catch (error: any) {
console.error(`storageService.initializeDatabase: ${error.message || error}`, error);
throw new Error(`storageService.initializeDatabase: ${error.message || error}`);
}
}
async query(sql: string, params: any[] = []): Promise<any> {
try {
return await this.db.query(sql, params);
} catch (err) {
console.error(`执行查询 ${sql} 失败:`, err);
throw err;
}
}
async run(sql: string, params: any[] = []): Promise<any> {
try {
return await this.db.run(sql, params);
} catch (err) {
console.error(`执行语句 ${sql} 失败:`, err);
throw err;
}
}
async execute(sql: string): Promise<any> {
try {
return await this.db.execute(sql);
} catch (err) {
console.error(`执行语句 ${sql} 失败:`, err);
throw err;
}
}
}
export default StorageService;

View File

@ -0,0 +1,237 @@
/* Ionic Variables and Theming. For more info, please see:
http://ionicframework.com/docs/theming/ */
/** Ionic CSS Variables **/
:root {
--ion-background-color: #ffffff;
/** primary **/
--ion-color-primary: #3880ff;
--ion-color-primary-rgb: 56, 128, 255;
--ion-color-primary-contrast: #ffffff;
--ion-color-primary-contrast-rgb: 255, 255, 255;
--ion-color-primary-shade: #3171e0;
--ion-color-primary-tint: #4c8dff;
/** secondary **/
--ion-color-secondary: #3dc2ff;
--ion-color-secondary-rgb: 61, 194, 255;
--ion-color-secondary-contrast: #e1dfdf;
--ion-color-secondary-contrast-rgb: 255, 255, 255;
--ion-color-secondary-shade: #36abe0;
--ion-color-secondary-tint: #50c8ff;
/** tertiary **/
--ion-color-tertiary: #5260ff;
--ion-color-tertiary-rgb: 82, 96, 255;
--ion-color-tertiary-contrast: #e3e3e3;
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
--ion-color-tertiary-shade: #4854e0;
--ion-color-tertiary-tint: #6370ff;
/** success **/
--ion-color-success: #2dd36f;
--ion-color-success-rgb: 45, 211, 111;
--ion-color-success-contrast: #ededed;
--ion-color-success-contrast-rgb: 255, 255, 255;
--ion-color-success-shade: #28ba62;
--ion-color-success-tint: #42d77d;
/** warning #222428 **/
--ion-color-warning: #ffc409;
--ion-color-warning-rgb: 255, 196, 9;
--ion-color-warning-contrast: #222428;
--ion-color-warning-contrast-rgb: 0, 0, 0;
--ion-color-warning-shade: #e0ac08;
--ion-color-warning-tint: #ffca22;
/** danger **/
--ion-color-danger: #eb445a;
--ion-color-danger-rgb: 235, 68, 90;
--ion-color-danger-contrast: #ffffff;
--ion-color-danger-contrast-rgb: 255, 255, 255;
--ion-color-danger-shade: #cf3c4f;
--ion-color-danger-tint: #ed576b;
/** dark #000000 **/
--ion-color-dark: #000000;
--ion-color-dark-rgb: 34, 36, 40;
--ion-color-dark-contrast: #e1e1e1;
--ion-color-dark-contrast-rgb: 255, 255, 255;
--ion-color-dark-shade: #1e2023;
--ion-color-dark-tint: #383a3e;
/** medium **/
--ion-color-medium: #92949c;
--ion-color-medium-rgb: 146, 148, 156;
--ion-color-medium-contrast: #e6e6e6;
--ion-color-medium-contrast-rgb: 255, 255, 255;
--ion-color-medium-shade: #808289;
--ion-color-medium-tint: #9d9fa6;
/** light **/
--ion-color-light: #e8e8e8;
--ion-color-light-rgb: 244, 245, 248;
--ion-color-light-contrast: #000000;
--ion-color-light-contrast-rgb: 0, 0, 0;
--ion-color-light-shade: #d7d8da;
--ion-color-light-tint: #f5f6f9;
}
@media (prefers-color-scheme: dark) {
/*
* Dark Colors
* -------------------------------------------
*/
body {
--ion-color-primary: #428cff;
--ion-color-primary-rgb: 66,140,255;
--ion-color-primary-contrast: #ffffff;
--ion-color-primary-contrast-rgb: 255,255,255;
--ion-color-primary-shade: #3a7be0;
--ion-color-primary-tint: #5598ff;
--ion-color-secondary: #50c8ff;
--ion-color-secondary-rgb: 80,200,255;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255,255,255;
--ion-color-secondary-shade: #46b0e0;
--ion-color-secondary-tint: #62ceff;
--ion-color-tertiary: #6a64ff;
--ion-color-tertiary-rgb: 106,100,255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255,255,255;
--ion-color-tertiary-shade: #5d58e0;
--ion-color-tertiary-tint: #7974ff;
--ion-color-success: #2fdf75;
--ion-color-success-rgb: 47,223,117;
--ion-color-success-contrast: #000000;
--ion-color-success-contrast-rgb: 0,0,0;
--ion-color-success-shade: #29c467;
--ion-color-success-tint: #44e283;
--ion-color-warning: #ffd534;
--ion-color-warning-rgb: 255,213,52;
--ion-color-warning-contrast: #000000;
--ion-color-warning-contrast-rgb: 0,0,0;
--ion-color-warning-shade: #e0bb2e;
--ion-color-warning-tint: #ffd948;
--ion-color-danger: #ff4961;
--ion-color-danger-rgb: 255,73,97;
--ion-color-danger-contrast: #ffffff;
--ion-color-danger-contrast-rgb: 255,255,255;
--ion-color-danger-shade: #e04055;
--ion-color-danger-tint: #ff5b71;
--ion-color-dark: #dfdfdf;
--ion-color-dark-rgb: 244,245,248;
--ion-color-dark-contrast: #141414;
--ion-color-dark-contrast-rgb: 0,0,0;
--ion-color-dark-shade: #b5b5b5;
--ion-color-dark-tint: #dedfe0;
--ion-color-medium: #989aa2;
--ion-color-medium-rgb: 152,154,162;
--ion-color-medium-contrast: #000000;
--ion-color-medium-contrast-rgb: 0,0,0;
--ion-color-medium-shade: #86888f;
--ion-color-medium-tint: #a2a4ab;
--ion-color-light: #222428;
--ion-color-light-rgb: 34,36,40;
--ion-color-light-contrast: #ffffff;
--ion-color-light-contrast-rgb: 255,255,255;
--ion-color-light-shade: #1e2023;
--ion-color-light-tint: #383a3e;
}
/*
* iOS Dark Theme #111111
* -------------------------------------------
*/
.ios body {
--ion-background-color: #000000;
--ion-background-color-rgb: 0,0,0;
--ion-text-color: #bfcbc7;
--ion-text-color-rgb: 255,255,255;
--ion-color-step-50: #0d0d0d;
--ion-color-step-100: #1a1a1a;
--ion-color-step-150: #262626;
--ion-color-step-200: #333333;
--ion-color-step-250: #404040;
--ion-color-step-300: #4d4d4d;
--ion-color-step-350: #595959;
--ion-color-step-400: #666666;
--ion-color-step-450: #737373;
--ion-color-step-500: #808080;
--ion-color-step-550: #8c8c8c;
--ion-color-step-600: #999999;
--ion-color-step-650: #a6a6a6;
--ion-color-step-700: #b3b3b3;
--ion-color-step-750: #bfbfbf;
--ion-color-step-800: #cccccc;
--ion-color-step-850: #d9d9d9;
--ion-color-step-900: #e6e6e6;
--ion-color-step-950: #f2f2f2;
--ion-item-background: #000000;
--ion-card-background: #1c1c1d;
}
.ios ion-modal {
--ion-background-color: var(--ion-color-step-50);
--ion-toolbar-background: var(--ion-color-step-100);
--ion-toolbar-border-color: var(--ion-color-step-150);
}
/*
* Material Design Dark Theme
* -------------------------------------------
*/
.md body {
--ion-background-color: #000000;
--ion-background-color-rgb: 18,18,18;
--ion-text-color: #dfdede;
--ion-text-color-rgb: 255,255,255;
--ion-border-color: #222222;
--ion-color-step-50: #1e1e1e;
--ion-color-step-100: #2a2a2a;
--ion-color-step-150: #363636;
--ion-color-step-200: #414141;
--ion-color-step-250: #4d4d4d;
--ion-color-step-300: #595959;
--ion-color-step-350: #656565;
--ion-color-step-400: #717171;
--ion-color-step-450: #7d7d7d;
--ion-color-step-500: #898989;
--ion-color-step-550: #949494;
--ion-color-step-600: #a0a0a0;
--ion-color-step-650: #acacac;
--ion-color-step-700: #b8b8b8;
--ion-color-step-750: #c4c4c4;
--ion-color-step-800: #d0d0d0;
--ion-color-step-850: #dbdbdb;
--ion-color-step-900: #e7e7e7;
--ion-color-step-950: #f3f3f3;
--ion-item-background: #000000;
--ion-toolbar-background: #000000;
--ion-tab-bar-background: #000000;
--ion-card-background: #1e1e1e;
}
}

View File

@ -0,0 +1,23 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"jsx": "preserve",
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["ESNext", "DOM"],
"skipLibCheck": true,
"noEmit": true,
"paths": {
"@/*": ["./src/*"],
"bare-plugin": ["./bare-plugin/dist/plugin"]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}

View File

@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

View File

@ -0,0 +1,73 @@
import legacy from '@vitejs/plugin-legacy'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import { defineConfig } from 'vite'
import Components from 'unplugin-vue-components/vite'
import AutoImport from 'unplugin-auto-import/vite'
import Pages from 'vite-plugin-pages'
import { VitePWA } from 'vite-plugin-pwa'
// import inject from "@rollup/plugin-inject";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
legacy(),
Components(),
// inject({
// Buffer: ["buffer", "Buffer"],
// }),
AutoImport({
imports: ['vue', 'vue-router'],
dirs: [
'src/composables',
],
dts: 'src/auto-imports.d.ts',
}),
Pages(),
VitePWA({
registerType: 'autoUpdate',
workbox: {
maximumFileSizeToCacheInBytes: 5000 * 1024 * 1024,
},
}),
],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
crypto: 'crypto-browserify',
buffer: "buffer",
},
},
define: {
global: "window",
},
build: {
sourcemap: true,
rollupOptions: {
external: ['text-encoding'],
output:{
manualChunks(id) {
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString();
}
}
}
}
},
// test: {
// globals: true,
// environment: 'jsdom'
// }
})

19
examples/relay.service Normal file
View File

@ -0,0 +1,19 @@
[Unit]
Description=GUN relay
Documentation=https://gun.eco
After=network.target
[Install]
WantedBy=multi-user.target
[Service]
Environment=PATH=/usr/bin:/usr/local/bin
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
StartLimitBurst=999999
StartLimitIntervalSec=999999
Restart=always
ExecStart=node examples/http.js 80
# Environment=NODE_ENV=production
WorkingDirectory=

1104
examples/smoothie.js Normal file

File diff suppressed because it is too large Load Diff

22
examples/start.js.html Normal file
View File

@ -0,0 +1,22 @@
/*<!DOCTYPE html>
<html>
<head></head>
<body></body>
<script>// */
;(function(){try{
if(typeof window == "undefined"){ return }
var url = location.hash.slice(1);
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(e){
if(4 != xhr.readyState){ return }
var d = document, doc = d.createElement("html");
doc.innerHTML = xhr.responseText;
var head = doc.getElementsByTagName('head')[0] || doc.appendChild(d.createElement('head'));
var base = doc.getElementsByTagName('base')[0] || head.appendChild(d.createElement('base'));
base.href = url;
document.write(doc.outerHTML);
};
xhr.open('GET', url, true);
xhr.send(null);
}catch(e){document.write(''+e)}}());
//</script></html>

153
examples/stats.html Normal file
View File

@ -0,0 +1,153 @@
<!DOCTYPE HTML>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="./style.css">
</head>
<body>
<style>
body {
font-family: helvetica;
background-color: rgb(25,25,25);
color: rgb(80,135,25) !important;
text-shadow: 1px 1px 20px rgb(80,150,25);
}
.label {
position: absolute;
left: 0.5em;
top: 1.75em;
}
.input {
height: 30px;
padding:10px;
background-color: rgb(50,50,50);
color: rgb(250,50,50);
}
.tall { height: 5em; }
</style>
<div class="center"><span class="shout" id="peers">0</span> peers <span class="shout" id="time">0</span> min <span class="shout" id="nodes">0</span> nodes <span class="shout" id="hours">0</span> hours <span class="shout" id="block">0</span> block <span class="shout" id="stack">0</span> stack</div>
<input id="url" class="center input crack" placeholder="enter peer stats source url">
<div class="center row charts">
</div>
<div class="model none">
<div class="chart"><span class="label"></span><canvas class="tall row"></canvas></div>
</div>
<script src="./jquery.js"></script>
<script src="./smoothie.js" charset="utf-8"></script>
<script>
if(window.location.search){url.value = window.location.search.split("?")[1]}
var up, br = 0, bt = 0, tmp;
var fetchData = async function(){
// fetch the data from server
var S = +new Date;
var data = await (await fetch(url.value||(location.origin+'/gun/stats.radata'), {method: 'GET',mode: 'cors'})).json();
$('#block').text(((br += (+new Date - S)/1000) / ++bt).toFixed(1));
data.over = (data.over/1000) || 15;
$('#stack').text((data.cpu||'').stack);
$('#peers').text(data.peers.count);
$('#time').text((data.peers.time / 1000 / 60).toFixed(0));
$('#nodes').text(data.node.count);
$('#hours').text((data.up.time / 60 / 60).toFixed(1));
if(data.up.time === up){ console.log("up same as before") } up = data.up.time;
;(async function(){ try{
Stats('peers#').line.append(+new Date, data.peers.count);
return;
Stats('cpu%');
tmp = await (await fetch(new Request(location.origin+'/gun/stats.top.radata'), {method: 'GET',mode: 'cors'})).text();
tmp = parseFloat((tmp.split('\n').filter(l => l.indexOf('node')+1)[0]||'').split(/\s+/).slice(-4)[0]||'0');
Stats('cpu%').line.append(+new Date, tmp);
}catch(e){console.log(e)}}());
Stats('memory').line.append(+new Date, data.memory.heapTotal / 1024 / 1024);
try{ Stats('dam # in/s').line.append(+new Date, Math.round(data.dam.in.count / data.over)); }catch(e){}
try{ Stats('dam in MB/s').line.append(+new Date, data.dam.in.done / 1024 / 1024 / data.over); }catch(e){}
try{ Stats('dam # out/s').line.append(+new Date, Math.round(data.dam.out.count / data.over)); }catch(e){}
try{ Stats('dam out MB/s').line.append(+new Date, data.dam.out.done / 1024 / 1024 / data.over); }catch(e){}
console.log('data',data);
//fetch keys in all, these may be dynamically changing
//for each key, check if we already have created a time series, if not, create it and add it
// to the chart corredsponding to the unit of measure
$.each(data.all, function(key, arr){
var chart = Stats(key);
// get data and append to line
// get the arrays inside the key
//for each array append the data to the line
for(var i in arr) {
// append data [timestamp], [data]
chart.line.append(arr[i][0], arr[i][1]);
}
});
}
//setInterval(fetchData, 15 * 1000);
setInterval(fetchData, 5000);
fetchData();
function Stats(key, chart){
// if we have already created, get data to append to it.
if(chart = Stats[key]){
return chart;
}
// create a new Series for this key
// add it into the map
chart = Stats[key] = new SmoothieChart({millisPerPixel:500, limitFPS: 16, responsive: true, minValue: 0, grid:{strokeStyle:'rgba(100%,100%,100%,0.2)'},labels:{fontSize:20}, grid: {verticalSections: 0, millisPerLine: 15000 * 4 /*, strokeStyle:'rgb(125, 0, 0)'*/}});
chart.line = new TimeSeries();
chart.addTimeSeries(chart.line,{ strokeStyle:'rgb('+Math.random()*255+', '+Math.random()*255+','+Math.random()*255+')', lineWidth:5 });
chart.canvas = $('.model').find('.chart').clone(true).appendTo('.charts');
chart.canvas.find('span').text(key);
chart.streamTo(chart.canvas.find('canvas').get(0), 15 * 1000);
chart.line.append(0, 0);
// check first two characters of key to determine other charts to add this in
// tbd later
return chart;
}
;(function(){
if('https' != (''+location).slice(0,5) && "localhost" != location.hostname){
$('body').append("<button id='https'>click here to try generating https certs</button>");
if(/^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|$)){4}$/.test(location.hostname)){
$('#https').text("Link this IP address to a Domain by adding an `A Record` to your DNS settings that point to `"+ location.hostname +"` (we recommend the `name/host` be any subdomain you want, like `relay`, but if you want the root domain itself to directly point here use `*`). Then come back here on the domain & click this button to generate HTTPS certificates.");
return;
}
$('body').append("<input id='email' placeholder='email'/>");
$('#https').on('click', function(){
$(this).text("look at console.log for errors, if none, try https");
var gun = GUN(location.origin + '/gun');
if(!$('#email').val()){
$(this).text("email necessary for certs! Type it in & click here again.");
return;
}
setTimeout(function(){
gun._.opt.mesh.say({dam: 'service', try: 'https', email: $('#email').val(), domain: location.hostname});
setTimeout(function(){
if(gun._.opt.mesh.near){ return }
$('#https').text("It might have worked! try HTTPS!");
}, 9000);
}, 999);
});
}
}());
/*
Notes to Self about Debugging:
1. Read Disks can spike up to 1min, I suspect other operations are blocking it from resolving as fast as it otherwise would.
2. JSON parsing/stringifying sometimes way slower than other times, why?
3. Looks like RAD lex read is not optimized.
4. got prep + got emit = non-RAD problems, compare against read disk & got differentials (should be same).
5. Radix map/place ops could be slow?
6. SINGLE MESSAGE PROCESS TIME occasionally is huge, should get emailed.
7. Watch out for get/put loops times, maybe indicating (5) issues?
*/
</script>
<script src="../gun.js"></script>
</body>
</html>

View File

@ -4,6 +4,7 @@ html, body {
position: relative;
line-height: 1.5;
font-size: 18pt;
f-ont-size: max(18pt, 2?vw);
}
div, ul, ol, li, p, span, form, button, input, textarea, img {
@ -14,6 +15,7 @@ div, ul, ol, li, p, span, form, button, input, textarea, img {
-webkit-transition: all 0.3s;
transition: all 0.3s;
box-sizing: border-box;
font: inherit;
}
a, button, input, textarea {
@ -21,19 +23,34 @@ a, button, input, textarea {
border: inherit;
color: inherit;
text-decoration: inherit;
outline: none;
}
input:not([type=button]):not([type=submit]), textarea {
width: 100%;
}
input, textarea {
width: 100%;
a:focus, button:focus, input[type=button]:focus, input[type=submit]:focus {
animation: pulse 2s infinite;
}
ul, li {
list-style: none;
}
p {
padding: 0;
}
p + p {
padding-top: 0;
}
[contenteditable=true]:empty:before {
content: attr(placeholder);
}
::placeholder, .hint {
color: inherit;
opacity: 0.3;
}
.model, .none { display: none }
.hide {
@ -42,11 +59,10 @@ ul, li {
transition: all 2s;
}
.full, .page {
.full {
width: 100%;
min-height: 100vh;
}
.max {
max-width: 48em;
}
@ -60,6 +76,25 @@ ul, li {
min-width: 12em;
}
.row {
width: 100%;
}
.row::after {
content: "";
display: block;
clear: both;
}
.col {
max-width: 24em;
min-width: 12em;
}
.center {
text-align: center;
vertical-align: middle;
margin-left: auto;
margin-right: auto;
}
.right {
float: right;
text-align: right;
@ -68,12 +103,6 @@ ul, li {
float: left;
text-align: left;
}
.center {
text-align: center;
vertical-align: middle;
margin-left: auto;
margin-right: auto;
}
.mid {
margin-left: auto;
margin-right: auto;
@ -85,34 +114,27 @@ ul, li {
vertical-align: bottom;
}
.rim { margin: 2%; }
.gap { padding: 3%; }
.rim { margin: 1%; }
.gap {
padding: 3%;
padding: clamp(0.5em, 3%, 1.5em);
}
.stack { line-height: 0; }
.crack { margin-bottom: 1%; }
.sit { margin-bottom: 0; }
.row { width: 100%; }
.col {
max-width: 24em;
min-width: 12em;
}
.focus {
margin-left: auto;
margin-right: auto;
float: none;
clear: both;
}
.unit, .symbol {
display: inline-block;
vertical-align: inherit;
}
.leak { overflow: visible; }
.hold { overflow: hidden; }
.act {
display: block;
/*display: block;*/
font-weight: normal;
text-decoration: none;
-webkit-transition: all 0.3s;
@ -120,6 +142,10 @@ ul, li {
cursor: pointer;
}
.unit, .symbol {
display: inline-block;
vertical-align: inherit;
}
.sap { border-radius: 0.1em; }
.jot { border-bottom: 1px dashed #95B2CA; }
@ -131,11 +157,10 @@ ul, li {
font-size: 6.5vmax;
}
.red { background: #ea3224; }
.green { background: #33cc33; }
.blue { background: #4D79D8; }
.yellow { background: #f2b919; }
.yellow { background: #d3a438; }
.black { background: black; }
.white { background: white; }
@ -145,7 +170,7 @@ ul, li {
.redt { color: #ea3224; }
.greent { color: #33cc33; }
.bluet { color: #4D79D8; }
.yellowt { color: #f2b919; }
.yellowt { color: #d3a438; }
.blackt { color: black; }
.whitet { color: white; }
@ -156,13 +181,13 @@ ul, li {
} @keyframes hue {
0% {background-color: #4D79D8;}
25% {background-color: #33cc33;}
50% {background-color: #f2b919;}
50% {background-color: #d3a438;}
75% {background-color: #ea3224;}
100% {background-color: #4D79D8;}
} @-webkit-keyframes hue {
0% {background-color: #4D79D8;}
25% {background-color: #33cc33;}
50% {background-color: #f2b919;}
50% {background-color: #d3a438;}
75% {background-color: #ea3224;}
100% {background-color: #4D79D8;}
}
@ -174,13 +199,13 @@ ul, li {
} @keyframes huet {
0% {color: #4D79D8;}
25% {color: #33cc33;}
50% {color: #f2b919;}
50% {color: #d3a438;}
75% {color: #ea3224;}
100% {color: #4D79D8;}
} @-webkit-keyframes huet {
0% {color: #4D79D8;}
25% {color: #33cc33;}
50% {color: #f2b919;}
50% {color: #d3a438;}
75% {color: #ea3224;}
100% {color: #4D79D8;}
}
@ -193,13 +218,13 @@ ul, li {
0% {background-color: #ea3224;}
25% {background-color: #4D79D8;}
50% {background-color: #33cc33;}
75% {background-color: #f2b919;}
75% {background-color: #d3a438;}
100% {background-color: #ea3224;}
} @-webkit-keyframes hue2 {
0% {background-color: #ea3224;}
25% {background-color: #4D79D8;}
50% {background-color: #33cc33;}
75% {background-color: #f2b919;}
75% {background-color: #d3a438;}
100% {background-color: #ea3224;}
}
@ -211,13 +236,13 @@ ul, li {
0% {color: #ea3224;}
25% {color: #4D79D8;}
50% {color: #33cc33;}
75% {color: #f2b919;}
75% {color: #d3a438;}
100% {color: #ea3224;}
} @-webkit-keyframes huet2 {
0% {color: #ea3224;}
25% {color: #4D79D8;}
50% {color: #33cc33;}
75% {color: #f2b919;}
75% {color: #d3a438;}
100% {color: #ea3224;}
}
@ -227,13 +252,13 @@ ul, li {
animation: hue3 900s infinite;
} @keyframes hue3 {
0% {background-color: #33cc33;}
25% {background-color: #f2b919;}
25% {background-color: #d3a438;}
50% {background-color: #ea3224;}
75% {background-color: #4D79D8;}
100% {background-color: #33cc33;}
} @-webkit-keyframes hue3 {
0% {background-color: #33cc33;}
25% {background-color: #f2b919;}
25% {background-color: #d3a438;}
50% {background-color: #ea3224;}
75% {background-color: #4D79D8;}
100% {background-color: #33cc33;}
@ -245,52 +270,52 @@ ul, li {
animation: huet3 900s infinite;
} @keyframes huet3 {
0% {color: #33cc33;}
25% {color: #f2b919;}
25% {color: #d3a438;}
50% {color: #ea3224;}
75% {color: #4D79D8;}
100% {color: #33cc33;}
} @-webkit-keyframes huet3 {
0% {color: #33cc33;}
25% {color: #f2b919;}
25% {color: #d3a438;}
50% {color: #ea3224;}
75% {color: #4D79D8;}
100% {color: #33cc33;}
}
.hue4 {
background: #f2b919;
background: #d3a438;
-webkit-animation: hue4 900s infinite;
animation: hue4 900s infinite;
} @keyframes hue4 {
0% {background-color: #f2b919;}
0% {background-color: #d3a438;}
25% {background-color: #ea3224;}
50% {background-color: #4D79D8;}
75% {background-color: #33cc33;}
100% {background-color: #f2b919;}
100% {background-color: #d3a438;}
} @-webkit-keyframes hue4 {
0% {background-color: #f2b919;}
0% {background-color: #d3a438;}
25% {background-color: #ea3224;}
50% {background-color: #4D79D8;}
75% {background-color: #33cc33;}
100% {background-color: #f2b919;}
100% {background-color: #d3a438;}
}
.huet4 {
color: #f2b919;
color: #d3a438;
-webkit-animation: huet4 900s infinite;
animation: huet4 900s infinite;
} @keyframes huet4 {
0% {color: #f2b919;}
0% {color: #d3a438;}
25% {color: #ea3224;}
50% {color: #4D79D8;}
75% {color: #33cc33;}
100% {color: #f2b919;}
100% {color: #d3a438;}
} @-webkit-keyframes huet4 {
0% {color: #f2b919;}
0% {color: #d3a438;}
25% {color: #ea3224;}
50% {color: #4D79D8;}
75% {color: #33cc33;}
100% {color: #f2b919;}
100% {color: #d3a438;}
}
.pulse {
@ -313,4 +338,15 @@ ul, li {
} @keyframes joy {
0% {background-position: 0 0;}
100% {background-position: -2800px 0;}
}
.visually-hidden {
border: 0;
clip: rect(1px, 1px, 1px, 1px);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}

View File

@ -1,83 +1,150 @@
<!DOCTYPE html>
<html>
<head>
<title>Think</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
<link rel="stylesheet" type="text/css" href="/style.css">
</head>
<body>
<div id="think" class="hue page">
<link href='https://fonts.googleapis.com/css?family=Alegreya+Sans:300italic' rel='stylesheet' type='text/css'>
<style>
#think {
font-size: 24pt;
font-size: 6vmin;
font-family: 'Alegreya Sans', sans-serif;
color: white;
}
#think li {
width: 90%;
margin-top: 0.3em;
border-bottom: 1px dashed white;
}
#think .add {
width: 1em;
height: 1em;
line-height: 1em;
padding: 0.5em;
font-family: Tahoma, arial;
text-align: center;
border-radius: 50%;
}
#think ul, #think li {
list-style-type: circle;
margin-left: 0.5em;
}
</style>
<div class="pad whitet" style="width: 75%;">
<div style="margin-top: 2%;">
<div class="rubric left center">Add a Thought...</div>
<a href="#" class="right huet white act add">+</a>
</div>
<ul>
</ul>
</div>
<script src="/jquery.js"></script>
<script src="/gun.js"></script>
<script src="/gun/nts.js"></script>
<script>
// Check out the interactive tutorial
// for how to build a simplified version
// of this example: https://scrimba.com/c/cW2Vsa
var gun = Gun(location.origin+'/gun');
var think = gun.get('think/' + location.hash.slice(1));
var typing, throttle;
$('.add').on('click', function(){
$('<li>').attr('contenteditable', true).prependTo('ul');
});
$(document).on('keyup', "[contenteditable=true]", function(){
var li = $(this), id = li.attr('id');
if(!id){
li.attr('id', id = Gun.text.random());
}
typing = id;
clearTimeout(throttle);
throttle = setTimeout(function(){
think.get(id).put(li.text());
typing = false;
},10);
});
think.map().on(function(thought, id){
var li = $('#'+id)[0] || $('<li>').attr('id', id).attr('contenteditable', true).prependTo('ul');
if(thought){
if(id === typing){ return }
$(li).text(thought);
} else {
$(li).hide();
}
});
</script>
</div>
</div>
</body>
<head>
<title>Think</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
<link rel="stylesheet" type="text/css" href="/style.css">
<link href='https://fonts.googleapis.com/css?family=Alegreya+Sans:300italic' rel='stylesheet' type='text/css'>
<style>
.thought {
font-family: 'Alegreya Sans', sans-serif;
}
.thought__heading {
text-align: center;
margin-top: 0;
margin-bottom: 0;
color: white;
}
.thought__form-container {
position: fixed;
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 10px 20px;
top: 0;
z-index: 1;
}
.thought__item {
width: 100%;
max-width: 900px;
}
.thought__input {
flex: 1;
font-family: 'Alegreya Sans', sans-serif;
font-size: 25px;
font-weight: 500;
padding: 15px;
width: 100%;
margin-bottom: 10px;
background-color: white;
border-radius: 5px;
}
.thought__add {
width: 30px;
height: 30px;
font-family: Tahoma, arial;
text-align: center;
border-radius: 50%;
background-color: white;
font-size: 25px;
font-weight: 700;
}
.thought__add:hover::after {
background-color: rgba(0,0,0,0.2);
}
.thought__add:focus::after {
background-color: rgba(0,0,0,0.2);
}
.thought__add::after {
content: '';
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
border-radius: 50%;
transition: background-color 0.3s;
background-color: rgba(0,0,0,0);
}
.thought__list {
list-style-type: circle;
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
overflow-y: auto;
padding: 90px 20px;
width: 100%;
background-color: rgba(0, 0, 0, 0.2);
min-height: 100vh;
}
@media (max-width: 567px) {
.thought__heading {
font-size: 30px;
}
}
</style>
</head>
<body>
<div class="thought hue page">
<div class="thought__form-container hue">
<h2 id='title' class="thought__heading hue">Add a thought...</h2>
<button class="thought__add say huet">
<span aria-hidden="true">+</span>
<span class="visually-hidden">Add thought</span>
</button>
</div>
<ul class="thought__list">
</ul>
</div>
<script src="/jquery.js"></script>
<script src="/gun.js"></script>
<script>
// Check out the interactive tutorial
// for how to build a simplified version
// of this example: https://scrimba.com/c/cW2Vsa
var gun = Gun(location.origin + '/gun');
var think = gun.get('think1/' + location.hash.slice(1));
var thoughtItemStr = function(id) { return '<li class="thought__item"><label class="visually-hidden" for="' + id + '">Thought</label><input id="' + id + '" class="thought__input huet"><li/>'}
var typing, throttle;
$('.thought__add').on('click', function () {
$(thoughtItemStr('')).prependTo('.thought__list').find('.thought__input').focus();
});
$(document).on('keyup', '.thought__input', function () {
var input = $(this), id = input.attr('id');
if (!id) {
input.attr('id', id = String.random());
}
typing = id;
clearTimeout(throttle);
throttle = setTimeout(function () {
think.get(id).put(input.val());
typing = false;
}, 10);
});
think.map().on(function (thought, id) {
var li = $('#' + id).parent()[0] || $(thoughtItemStr(id)).prependTo('.thought__list');
if (thought) {
if (id === typing) { return }
$(li).find('.thought__input').val(thought);
} else {
$(li).hide();
}
});
</script>
</div>
</body>

View File

@ -0,0 +1,77 @@
<!DOCTYPE html>
<html>
<head>
<title>Think</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
</head>
<body>
<video id="video" width="100%"></video>
<center>
<button id="record">Record</button>
<button id="play">Play</button>
</center>
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
<script>
const gun = Gun(`${window.location.origin}/gun`)
const record = { recorder: null, recording: false }
const video = document.querySelector('#video')
const playButton = document.querySelector('#play')
const recordButton = document.querySelector('#record')
recordButton.addEventListener('click', () => {
console.log(record)
if (!record.ing) {
return record.stream()
}
recordButton.innerText = 'Record'
if (record.ing.stop) { record.ing.stop() }
record.ing = false
}, false)
record.stream = () => {
navigator.mediaDevices.getDisplayMedia({ video: true }).then(stream => {
const chunks = [] // we have a stream, we can record it
record.ing = new MediaRecorder(stream)
record.ing.ondataavailable = eve => chunks.push(eve.data)
record.ing.onstop = () => record.save(new Blob(chunks))
record.ing.start()
recordButton.innerText = 'End'
}, err => { console.log(err) })
}
record.save = data => {
record.file = record.file || new FileReader()
record.file.readAsDataURL(data)
record.file.onloadend = () => {
let b64 = record.file.result
b64 = `data:video/webm${b64.slice(b64.indexOf(';'))}`
gun.get('test').get('screen').put(b64)
}
}
playButton.addEventListener('click', () => {
if (record.playing) {
playButton.innerText = 'Play'
video.pause()
record.playing = false
return
}
playButton.innerText = 'Stop'
record.playing = true
gun.get('test').get('screen').once(data => {
if (!data) { return }
video.src = data
video.play()
})
}, false)
</script>
</body>
</html>

View File

@ -1,53 +1,65 @@
<!DOCTYPE html>
<html>
<head>
<title>Think</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
</head>
<body>
<h1>Thoughts</h1>
<h1>Thoughts</h1>
<form id="form">
<input id="input">
<button>Add</button>
</form>
<form id="form">
<input id="input">
<button>Add</button>
</form>
<ul id="parentList"></ul>
<!-- <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script> -->
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
<link href="style.css" rel="stylesheet">
<script>
var gun = Gun().get('thoughts');
document.getElementById('form').addEventListener('submit', function (e) {
e.preventDefault(); // attaches event listener and prevent default form action
var data = document.getElementById('input').value;
gun.set(data);
document.getElementById('input').value = "";
});
gun.map().on(function (thought, id) {
var li = document.getElementById(id) || document.getElementById('parentList').insertAdjacentHTML('beforeend', '<li id =' + id + '> ' + thought + '</li>');
var $ = function (selector) {
return document.querySelector(selector);
};
// attach the event listener to the selected li items
var links = $('#parentList').getElementsByTagName('li');
for (var i = 0; i < links.length; i++) {
var link = links[i];
// console.log(link.innerHTML);
link.ondblclick = dynamicEvent;
if (link.innerHTML === " null" || link.innerHTML === " " || link.innerHTML === "") {
link.style.display = "none";
} else {
link.style.display = "list-item";
};
};
});
function dynamicEvent() {
gun.get(this.id).put(null);
this.innerHTML = document.getElementById(this.id.innerHTML);
if (this.innerHTML === " null" || this.innerHTML === " " || this.innerHTML === "") {
this.style.display = "none";
} else {
this.style.display = "list-item";
};
};
</script>
<ul id="parentList"></ul>
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
<script>
const gun = Gun(`${window.location.origin}/gun`).get('thoughts')
const parentList = document.getElementById('parentList')
const input = document.getElementById('input')
const form = document.getElementById('form')
const dynamicEvent = e => {
const target = e.target;
gun.get(target.id).put(null);
target.innerHTML = document.getElementById(target.id);
if (target.innerHTML === ' null' || target.innerHTML === ' ' || target.innerHTML === '') {
target.style.display = 'none';
} else {
target.style.display = 'list-item';
}
}
gun.map().on((thought, id) => {
parentList.insertAdjacentHTML('beforeend', `<li id =${id}> ${thought}</li>`);
const links = parentList.getElementsByTagName('li');
for (const link of links) {
if (link.innerHTML === ' null' || link.innerHTML === ' ' || link.innerHTML === '') {
link.style.display = 'none';
} else {
link.style.display = 'list-item';
}
link.ondblclick = dynamicEvent;
}
})
form.addEventListener('submit', e => {
e.preventDefault();
gun.set(input.value);
input.value = '';
})
</script>
</body>
</html>

View File

@ -0,0 +1,58 @@
<!DOCTYPE html>
<h1>User</h1>
<form id="sign">
<input id="alias" placeholder="username">
<input id="pass" type="password" placeholder="passphrase">
<input id="signup" type="button" value="sign up">
<input id="signin" type="button" value="sign in">
</form>
<ul id="list"></ul>
<form id="said">
<input id="say">
<input id="speak" type="submit" value="speak">
</form>
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/sea.js"></script>
<script>
const gun = Gun(`${window.location.origin}/gun`)
const user = gun.user().recall({ sessionStorage: true })
const alias = document.querySelector('#alias')
const pass = document.querySelector('#pass')
const sign = document.querySelector('#sign')
const signup = document.querySelector('#signup')
const signin = document.querySelector('#signin')
const said = document.querySelector('#said')
const say = document.querySelector('#say')
const ul = document.querySelector('#list')
function UI (say, id) {
const li = document.createElement('li')
li.setAttribute('id', id)
li.appendChild(document.createTextNode(say))
ul.appendChild(li)
return false
}
signup.addEventListener('click', () => user.create(alias.value, pass.value, () => user.auth(alias.value, pass.value)))
signin.addEventListener('click', () => user.auth(alias.value, pass.value))
said.addEventListener('submit', e => {
e.preventDefault()
// if(!user.is){ return }
user.get('said').set(say.value)
said.value = ''
})
gun.on('auth', () => {
sign.style.display = 'none'
user.get('said').map().on(UI)
})
</script>

View File

@ -0,0 +1,83 @@
<!DOCTYPE html>
<video id="video" width="100%" controls autoplay></video>
<center>
<input id="pass" placeholder="password">
Record <button class="record">Camera</button> or <button class="screen">Screen</button>
</center>
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/sea.js"></script>
<script>
const peers = [
`${window.location.origin}/gun`,
'https://gun-us.herokuapp.com/gun',
'https://gun-eu.herokuapp.com/gun'
]
const gun = Gun({ localStorage: true, peers })
const pass = document.querySelector('#pass')
const video = document.querySelector('#video')
const camera = document.querySelector('.record')
const screen = document.querySelector('.screen')
gun.get('test').get('video').on(async data => {
if (pass.value) { data = await SEA.decrypt(data, pass.value) }
video.setAttribute('src', data)
})
function record(type) {
function load(media) {
const chunks = []
record.ing = new MediaRecorder(media)
record.ing.ondataavailable = eve => { chunks.push(eve.data) }
record.ing.onstop = eve => { record.save(new Blob(chunks)) }
record.ing.start()
}
function error(err) { console.log(err) }
if (type === 'Camera') {
navigator.getMedia({ video: true, audio: true }, load, error)
}
if (type === 'Screen') {
navigator.mediaDevices.getDisplayMedia({ video: true, audio: true }).then(load, error)
}
}
function act(e) {
if (record.ing) {
if (record.ing.stop) { record.ing.stop() }
e.target.textContent = record.type
record.ing = false
return
}
record(record.type = e.target.textContent)
e.target.textContent = 'end'
}
record.save = data => {
record.file = record.file || new FileReader()
record.file.readAsDataURL(data)
record.file.onloadend = async () => {
const b64 = record.file.result
let b64formated = `data:video/webm${b64.slice(b64.indexOf(';'))}`
video.setAttribute('src', b64formated)
if (pass.value) { b64formated = await SEA.encrypt(b64formated, pass.value) }
gun.get('test').get('video').put(b64formated)
}
}
camera.addEventListener('click', e => act(e))
screen.addEventListener('click', e => act(e))
navigator.getMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia || navigator.msGetUserMedia)
</script>

View File

@ -9,7 +9,7 @@
</head>
<body>
<div id="app">
This is example of simple Vue plugin. It works exatcly same as the Vue instance data property, but the name is gunData.<br>
This is example of simple Vue plugin. It works exactly same as the Vue instance data property, but the name is gunData.<br>
The cool part is that every property in the gunData is realtime synced via gunDB to every other page viewer!<br>
<table>
<tr>

40
examples/wave.html Normal file
View File

@ -0,0 +1,40 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Wave example</title>
</head>
<body>
<button id="bPiano">Piano left</button>
<button id="bGuitar">Guitar itched</button>
<button id="bXylo">Xylophone slow</button>
<button id="bAll">All in one</button>
<script src="../lib/wave.js"></script>
<script>
function piano() {
wave(':piano:zxcvbn').balance(-1).play()
}
function guitar() {
wave(':guitar:zxcvbn').itch(0.5).play()
}
function xylo() {
wave(':xylophone:zxcvbn').pace(100).play()
}
function all() {
wave(`
:pace 400: :itch 0: :balance 0:
:piano: :balance -1: zxcvbn
:guitar: :balance 0: :itch 0.5: zxcvbn
:xylophone: :itch 0: :pace 100: zxcvbn
`).play()
}
document.querySelector('#bPiano').addEventListener("click", piano)
document.querySelector('#bGuitar').addEventListener("click", guitar)
document.querySelector('#bXylo').addEventListener("click", xylo)
document.querySelector('#bAll').addEventListener("click", all)
</script>
</body>
</html>

3
examples/webpack/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
package-lock.json
node_modules
public

View File

@ -0,0 +1,17 @@
{
"name": "webpack",
"version": "1.0.0",
"description": "webpack build example",
"scripts": {
"build": "webpack --devtool source-map --config webpack.config.js",
"prepare": "npm run build"
},
"author": "",
"license": "ISC",
"dependencies": {
"gun": "github:amark/gun",
"html-webpack-plugin": "^5.5.0",
"webpack": "^5.72.1",
"webpack-cli": "^4.9.2"
}
}

View File

@ -0,0 +1,9 @@
define(function(require, exports, module) {
var Gun = require("gun");
var gun = new Gun();
gun.get("hello").get("world").put("from gun").on((data, key) => console.log(data, key));
});

Some files were not shown because too many files have changed in this diff Show More