@rubytech/create-maxy-code 0.1.119 → 0.1.120
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/platform/neo4j/schema.cypher +93 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.d.ts +12 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js +34 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js.map +1 -1
- package/payload/platform/plugins/docs/references/visitor-graph.md +82 -0
- package/payload/platform/plugins/memory/references/schema-base.md +44 -0
- package/payload/server/{chunk-FJCI6X3H.js → chunk-HCYM5FLU.js} +2 -0
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/privacy.html +66 -0
- package/payload/server/public/v.js +191 -0
- package/payload/server/server.js +473 -85
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/* Visitor-graph pixel (Task 357).
|
|
2
|
+
*
|
|
3
|
+
* Single IIFE. Plain JS. No build step. ~3 KB minified.
|
|
4
|
+
*
|
|
5
|
+
* Loaded by hosted listing pages:
|
|
6
|
+
*
|
|
7
|
+
* <script defer src="https://<brand>/v.js" data-site="<slug>"></script>
|
|
8
|
+
*
|
|
9
|
+
* Emits to POST /v/event:
|
|
10
|
+
*
|
|
11
|
+
* pageview — once per session per URL (idempotent within 5s for SPA nav)
|
|
12
|
+
* click — DOM clicks on elements with data-track="<label>", or
|
|
13
|
+
* anchors flagged data-track-gallery / data-track-floorplan
|
|
14
|
+
* scroll — roll-up: one event per milestone {25,50,75,100} crossed
|
|
15
|
+
* dwell — on visibility change to hidden / pagehide, sendBeacon
|
|
16
|
+
*
|
|
17
|
+
* Identity: the server resolves identity from the mxy_v cookie. The pixel
|
|
18
|
+
* sends visitorId from localStorage (mints one if absent) as a fallback so
|
|
19
|
+
* cross-tab and same-browser-no-cookie cases still cluster.
|
|
20
|
+
*/
|
|
21
|
+
(function () {
|
|
22
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') return
|
|
23
|
+
var script = document.currentScript
|
|
24
|
+
if (!script) return
|
|
25
|
+
var siteSlug = script.getAttribute('data-site') || ''
|
|
26
|
+
var endpoint = new URL('/v/event', script.src).toString()
|
|
27
|
+
|
|
28
|
+
var VISITOR_KEY = 'mxy_visitor_id'
|
|
29
|
+
var SESSION_KEY = 'mxy_session_id'
|
|
30
|
+
var SESSION_PV_KEY = 'mxy_session_pv'
|
|
31
|
+
var LAST_PV_KEY = 'mxy_last_pv'
|
|
32
|
+
var SCROLL_KEY = 'mxy_scroll_depth'
|
|
33
|
+
var DWELL_KEY = 'mxy_dwell_start'
|
|
34
|
+
|
|
35
|
+
function randomId() {
|
|
36
|
+
if (window.crypto && window.crypto.getRandomValues) {
|
|
37
|
+
var buf = new Uint8Array(16)
|
|
38
|
+
window.crypto.getRandomValues(buf)
|
|
39
|
+
return Array.prototype.map.call(buf, function (b) {
|
|
40
|
+
return ('0' + b.toString(16)).slice(-2)
|
|
41
|
+
}).join('')
|
|
42
|
+
}
|
|
43
|
+
return String(Date.now()) + '_' + Math.random().toString(36).slice(2, 10)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getVisitorId() {
|
|
47
|
+
try {
|
|
48
|
+
var v = localStorage.getItem(VISITOR_KEY)
|
|
49
|
+
if (v) return v
|
|
50
|
+
var fresh = randomId()
|
|
51
|
+
localStorage.setItem(VISITOR_KEY, fresh)
|
|
52
|
+
return fresh
|
|
53
|
+
} catch (_e) {
|
|
54
|
+
return 'noltc-' + randomId()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getSessionId() {
|
|
59
|
+
try {
|
|
60
|
+
var s = sessionStorage.getItem(SESSION_KEY)
|
|
61
|
+
if (s) return s
|
|
62
|
+
var fresh = randomId()
|
|
63
|
+
sessionStorage.setItem(SESSION_KEY, fresh)
|
|
64
|
+
return fresh
|
|
65
|
+
} catch (_e) {
|
|
66
|
+
return 'nots-' + randomId()
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
var visitorId = getVisitorId()
|
|
71
|
+
var sessionId = getSessionId()
|
|
72
|
+
|
|
73
|
+
function send(payload) {
|
|
74
|
+
payload.site = siteSlug
|
|
75
|
+
payload.vid = visitorId
|
|
76
|
+
payload.sid = sessionId
|
|
77
|
+
payload.url = location.href
|
|
78
|
+
payload.path = location.pathname
|
|
79
|
+
payload.title = (document.title || '').slice(0, 256)
|
|
80
|
+
payload.ts = Date.now()
|
|
81
|
+
try {
|
|
82
|
+
var body = JSON.stringify(payload)
|
|
83
|
+
if (navigator.sendBeacon) {
|
|
84
|
+
var blob = new Blob([body], { type: 'application/json' })
|
|
85
|
+
if (navigator.sendBeacon(endpoint, blob)) return
|
|
86
|
+
}
|
|
87
|
+
fetch(endpoint, {
|
|
88
|
+
method: 'POST',
|
|
89
|
+
headers: { 'Content-Type': 'application/json' },
|
|
90
|
+
body: body,
|
|
91
|
+
credentials: 'include',
|
|
92
|
+
keepalive: true,
|
|
93
|
+
}).catch(function () { /* fire-and-forget */ })
|
|
94
|
+
} catch (_e) {
|
|
95
|
+
/* silently drop — pixel must never break the page */
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ─────────── pageview (idempotent within 5s for SPA nav) ───────────
|
|
100
|
+
function emitPageview() {
|
|
101
|
+
try {
|
|
102
|
+
var last = sessionStorage.getItem(LAST_PV_KEY)
|
|
103
|
+
if (last) {
|
|
104
|
+
var parsed = JSON.parse(last)
|
|
105
|
+
if (parsed && parsed.url === location.href && Date.now() - parsed.ts < 5000) {
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
sessionStorage.setItem(LAST_PV_KEY, JSON.stringify({ url: location.href, ts: Date.now() }))
|
|
110
|
+
sessionStorage.setItem(SESSION_PV_KEY, randomId()) // PageView id for milestone roll-up
|
|
111
|
+
sessionStorage.setItem(SCROLL_KEY, '0')
|
|
112
|
+
} catch (_e) { /* localStorage / sessionStorage disabled */ }
|
|
113
|
+
|
|
114
|
+
var pvId = ''
|
|
115
|
+
try { pvId = sessionStorage.getItem(SESSION_PV_KEY) || '' } catch (_e) {}
|
|
116
|
+
send({
|
|
117
|
+
type: 'pageview',
|
|
118
|
+
pvId: pvId,
|
|
119
|
+
referrer: document.referrer || '',
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ─────────── click tracking ───────────
|
|
124
|
+
document.addEventListener('click', function (ev) {
|
|
125
|
+
var el = ev.target && (ev.target.closest ? ev.target.closest('[data-track]') : null)
|
|
126
|
+
if (!el) return
|
|
127
|
+
var label = el.getAttribute('data-track') || 'unknown'
|
|
128
|
+
var pvId = ''
|
|
129
|
+
try { pvId = sessionStorage.getItem(SESSION_PV_KEY) || '' } catch (_e) {}
|
|
130
|
+
send({
|
|
131
|
+
type: 'click',
|
|
132
|
+
label: label.slice(0, 64),
|
|
133
|
+
pvId: pvId,
|
|
134
|
+
href: el.getAttribute('href') || '',
|
|
135
|
+
})
|
|
136
|
+
}, true)
|
|
137
|
+
|
|
138
|
+
// ─────────── scroll milestones (roll-up) ───────────
|
|
139
|
+
var scrollScheduled = false
|
|
140
|
+
function onScroll() {
|
|
141
|
+
if (scrollScheduled) return
|
|
142
|
+
scrollScheduled = true
|
|
143
|
+
requestAnimationFrame(function () {
|
|
144
|
+
scrollScheduled = false
|
|
145
|
+
var doc = document.documentElement
|
|
146
|
+
var max = Math.max(doc.scrollHeight, document.body.scrollHeight) - window.innerHeight
|
|
147
|
+
if (max <= 0) return
|
|
148
|
+
var pct = Math.floor(((window.scrollY || doc.scrollTop) / max) * 100)
|
|
149
|
+
var threshold = pct >= 100 ? 100 : pct >= 75 ? 75 : pct >= 50 ? 50 : pct >= 25 ? 25 : 0
|
|
150
|
+
if (threshold === 0) return
|
|
151
|
+
try {
|
|
152
|
+
var prev = parseInt(sessionStorage.getItem(SCROLL_KEY) || '0', 10) || 0
|
|
153
|
+
if (threshold <= prev) return
|
|
154
|
+
sessionStorage.setItem(SCROLL_KEY, String(threshold))
|
|
155
|
+
} catch (_e) {}
|
|
156
|
+
var pvId = ''
|
|
157
|
+
try { pvId = sessionStorage.getItem(SESSION_PV_KEY) || '' } catch (_e) {}
|
|
158
|
+
send({ type: 'scroll', depth: threshold, pvId: pvId })
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
window.addEventListener('scroll', onScroll, { passive: true })
|
|
162
|
+
|
|
163
|
+
// ─────────── dwell on unload / visibility change ───────────
|
|
164
|
+
try { sessionStorage.setItem(DWELL_KEY, String(Date.now())) } catch (_e) {}
|
|
165
|
+
function emitDwell() {
|
|
166
|
+
var start = 0
|
|
167
|
+
try { start = parseInt(sessionStorage.getItem(DWELL_KEY) || '0', 10) || 0 } catch (_e) {}
|
|
168
|
+
if (!start) return
|
|
169
|
+
var ms = Date.now() - start
|
|
170
|
+
if (ms < 250) return // ignore drive-bys
|
|
171
|
+
var pvId = ''
|
|
172
|
+
try { pvId = sessionStorage.getItem(SESSION_PV_KEY) || '' } catch (_e) {}
|
|
173
|
+
send({ type: 'dwell', ms: ms, pvId: pvId })
|
|
174
|
+
}
|
|
175
|
+
document.addEventListener('visibilitychange', function () {
|
|
176
|
+
if (document.visibilityState === 'hidden') emitDwell()
|
|
177
|
+
})
|
|
178
|
+
window.addEventListener('pagehide', emitDwell)
|
|
179
|
+
|
|
180
|
+
// SPA navigation hooks — many listing sites are client-routed.
|
|
181
|
+
var origPushState = history.pushState
|
|
182
|
+
history.pushState = function () {
|
|
183
|
+
var r = origPushState.apply(this, arguments)
|
|
184
|
+
setTimeout(emitPageview, 0)
|
|
185
|
+
return r
|
|
186
|
+
}
|
|
187
|
+
window.addEventListener('popstate', emitPageview)
|
|
188
|
+
|
|
189
|
+
// Boot.
|
|
190
|
+
emitPageview()
|
|
191
|
+
})()
|