parse-stack-next 5.0.1 → 5.1.0
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.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.yml +105 -0
- data/.github/ISSUE_TEMPLATE/feature_request.yml +67 -0
- data/.github/dependabot.yml +13 -0
- data/.github/workflows/codeql.yml +1 -1
- data/.github/workflows/docs.yml +3 -3
- data/.github/workflows/release.yml +14 -3
- data/.github/workflows/ruby.yml +1 -1
- data/.gitignore +1 -0
- data/.yardopts +19 -0
- data/CHANGELOG.md +792 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +8 -5
- data/README.md +15 -0
- data/Rakefile +5 -1
- data/docs/acl_clp_guide.md +553 -0
- data/docs/atlas_vector_search_guide.md +123 -22
- data/docs/client_sdk_guide.md +201 -5
- data/docs/usage_guide.md +21 -0
- data/docs/yard-template/default/fulldoc/html/css/common.css +1222 -0
- data/docs/yard-template/default/fulldoc/html/css/full_list.css +387 -0
- data/lib/parse/agent/tools.rb +153 -1
- data/lib/parse/cache/redis.rb +53 -0
- data/lib/parse/client/caching.rb +18 -1
- data/lib/parse/client.rb +79 -12
- data/lib/parse/embeddings/cohere.rb +143 -6
- data/lib/parse/embeddings/provider.rb +20 -2
- data/lib/parse/embeddings/voyage.rb +102 -0
- data/lib/parse/embeddings.rb +332 -1
- data/lib/parse/live_query/client.rb +167 -4
- data/lib/parse/live_query/configuration.rb +12 -0
- data/lib/parse/live_query/subscription.rb +55 -2
- data/lib/parse/live_query.rb +123 -1
- data/lib/parse/lock.rb +342 -0
- data/lib/parse/lock_backend.rb +308 -0
- data/lib/parse/model/classes/audience.rb +5 -0
- data/lib/parse/model/classes/installation.rb +122 -0
- data/lib/parse/model/classes/job_schedule.rb +3 -1
- data/lib/parse/model/classes/job_status.rb +4 -1
- data/lib/parse/model/classes/push_status.rb +4 -1
- data/lib/parse/model/classes/session.rb +7 -0
- data/lib/parse/model/classes/user.rb +204 -0
- data/lib/parse/model/core/create_lock.rb +28 -146
- data/lib/parse/model/core/embed_managed.rb +162 -13
- data/lib/parse/model/core/parse_reference.rb +17 -1
- data/lib/parse/model/core/querying.rb +26 -2
- data/lib/parse/model/file.rb +523 -18
- data/lib/parse/query.rb +31 -1
- data/lib/parse/stack/version.rb +1 -1
- data/lib/parse/stack.rb +98 -1
- data/parse-stack-next.gemspec +2 -2
- metadata +17 -7
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
body {
|
|
2
|
+
margin: 0;
|
|
3
|
+
font-family: "Lucida Sans", "Lucida Grande", Verdana, Arial, sans-serif;
|
|
4
|
+
font-size: 13px;
|
|
5
|
+
height: 101%;
|
|
6
|
+
overflow-x: hidden;
|
|
7
|
+
background: #fafafa;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
h1 {
|
|
11
|
+
padding: 12px 10px;
|
|
12
|
+
padding-bottom: 0;
|
|
13
|
+
margin: 0;
|
|
14
|
+
font-size: 1.4em;
|
|
15
|
+
}
|
|
16
|
+
.clear {
|
|
17
|
+
clear: both;
|
|
18
|
+
}
|
|
19
|
+
.fixed_header {
|
|
20
|
+
position: fixed;
|
|
21
|
+
background: #fff;
|
|
22
|
+
width: 100%;
|
|
23
|
+
padding-bottom: 10px;
|
|
24
|
+
margin-top: 0;
|
|
25
|
+
top: 0;
|
|
26
|
+
z-index: 9999;
|
|
27
|
+
height: 70px;
|
|
28
|
+
}
|
|
29
|
+
#search {
|
|
30
|
+
position: absolute;
|
|
31
|
+
right: 5px;
|
|
32
|
+
top: 9px;
|
|
33
|
+
padding-left: 24px;
|
|
34
|
+
}
|
|
35
|
+
#noresults {
|
|
36
|
+
padding: 7px 12px;
|
|
37
|
+
background: #fff;
|
|
38
|
+
}
|
|
39
|
+
#content.insearch #search,
|
|
40
|
+
#content.insearch #noresults {
|
|
41
|
+
background: url(data:image/gif;base64,R0lGODlhEAAQAPYAAP///wAAAPr6+pKSkoiIiO7u7sjIyNjY2J6engAAAI6OjsbGxjIyMlJSUuzs7KamppSUlPLy8oKCghwcHLKysqSkpJqamvT09Pj4+KioqM7OzkRERAwMDGBgYN7e3ujo6Ly8vCoqKjY2NkZGRtTU1MTExDw8PE5OTj4+PkhISNDQ0MrKylpaWrS0tOrq6nBwcKysrLi4uLq6ul5eXlxcXGJiYoaGhuDg4H5+fvz8/KKiohgYGCwsLFZWVgQEBFBQUMzMzDg4OFhYWBoaGvDw8NbW1pycnOLi4ubm5kBAQKqqqiQkJCAgIK6urnJyckpKSjQ0NGpqatLS0sDAwCYmJnx8fEJCQlRUVAoKCggICLCwsOTk5ExMTPb29ra2tmZmZmhoaNzc3KCgoBISEiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCAAAACwAAAAAEAAQAAAHaIAAgoMgIiYlg4kACxIaACEJCSiKggYMCRselwkpghGJBJEcFgsjJyoAGBmfggcNEx0flBiKDhQFlIoCCA+5lAORFb4AJIihCRbDxQAFChAXw9HSqb60iREZ1omqrIPdJCTe0SWI09GBACH5BAkIAAAALAAAAAAQABAAAAdrgACCgwc0NTeDiYozCQkvOTo9GTmDKy8aFy+NOBA7CTswgywJDTIuEjYFIY0JNYMtKTEFiRU8Pjwygy4ws4owPyCKwsMAJSTEgiQlgsbIAMrO0dKDGMTViREZ14kYGRGK38nHguHEJcvTyIEAIfkECQgAAAAsAAAAABAAEAAAB2iAAIKDAggPg4iJAAMJCRUAJRIqiRGCBI0WQEEJJkWDERkYAAUKEBc4Po1GiKKJHkJDNEeKig4URLS0ICImJZAkuQAhjSi/wQyNKcGDCyMnk8u5rYrTgqDVghgZlYjcACTA1sslvtHRgQAh+QQJCAAAACwAAAAAEAAQAAAHZ4AAgoOEhYaCJSWHgxGDJCQARAtOUoQRGRiFD0kJUYWZhUhKT1OLhR8wBaaFBzQ1NwAlkIszCQkvsbOHL7Y4q4IuEjaqq0ZQD5+GEEsJTDCMmIUhtgk1lo6QFUwJVDKLiYJNUd6/hoEAIfkECQgAAAAsAAAAABAAEAAAB2iAAIKDhIWGgiUlh4MRgyQkjIURGRiGGBmNhJWHm4uen4ICCA+IkIsDCQkVACWmhwSpFqAABQoQF6ALTkWFnYMrVlhWvIKTlSAiJiVVPqlGhJkhqShHV1lCW4cMqSkAR1ofiwsjJyqGgQAh+QQJCAAAACwAAAAAEAAQAAAHZ4AAgoOEhYaCJSWHgxGDJCSMhREZGIYYGY2ElYebi56fhyWQniSKAKKfpaCLFlAPhl0gXYNGEwkhGYREUywag1wJwSkHNDU3D0kJYIMZQwk8MjPBLx9eXwuETVEyAC/BOKsuEjYFhoEAIfkECQgAAAAsAAAAABAAEAAAB2eAAIKDhIWGgiUlh4MRgyQkjIURGRiGGBmNhJWHm4ueICImip6CIQkJKJ4kigynKaqKCyMnKqSEK05StgAGQRxPYZaENqccFgIID4KXmQBhXFkzDgOnFYLNgltaSAAEpxa7BQoQF4aBACH5BAkIAAAALAAAAAAQABAAAAdogACCg4SFggJiPUqCJSWGgkZjCUwZACQkgxGEXAmdT4UYGZqCGWQ+IjKGGIUwPzGPhAc0NTewhDOdL7Ykji+dOLuOLhI2BbaFETICx4MlQitdqoUsCQ2vhKGjglNfU0SWmILaj43M5oEAOwAAAAAAAAAAAA==)
|
|
42
|
+
no-repeat center left;
|
|
43
|
+
}
|
|
44
|
+
#full_list {
|
|
45
|
+
padding: 0;
|
|
46
|
+
list-style: none;
|
|
47
|
+
margin-left: 0;
|
|
48
|
+
margin-top: 80px;
|
|
49
|
+
font-size: 1.1em;
|
|
50
|
+
}
|
|
51
|
+
#full_list ul {
|
|
52
|
+
padding: 0;
|
|
53
|
+
}
|
|
54
|
+
#full_list li {
|
|
55
|
+
padding: 0;
|
|
56
|
+
margin: 0;
|
|
57
|
+
list-style: none;
|
|
58
|
+
}
|
|
59
|
+
#full_list li .item {
|
|
60
|
+
padding: 5px 5px 5px 12px;
|
|
61
|
+
}
|
|
62
|
+
#content.insearch #noresults {
|
|
63
|
+
margin-left: 7px;
|
|
64
|
+
}
|
|
65
|
+
#full_list li {
|
|
66
|
+
color: #666;
|
|
67
|
+
cursor: normal;
|
|
68
|
+
white-space: nowrap;
|
|
69
|
+
}
|
|
70
|
+
#full_list li.collapsed ul {
|
|
71
|
+
display: none;
|
|
72
|
+
}
|
|
73
|
+
#full_list li a.toggle {
|
|
74
|
+
cursor: default;
|
|
75
|
+
position: relative;
|
|
76
|
+
left: -5px;
|
|
77
|
+
top: 4px;
|
|
78
|
+
text-indent: -999px;
|
|
79
|
+
width: 10px;
|
|
80
|
+
height: 9px;
|
|
81
|
+
margin-left: -10px;
|
|
82
|
+
display: block;
|
|
83
|
+
float: left;
|
|
84
|
+
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK8AAACvABQqw0mAAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTM5jWRgMAAAAVdEVYdENyZWF0aW9uIFRpbWUAMy8xNC8wOeNZPpQAAAE2SURBVDiNrZTBccIwEEXfelIAHUA6CZ24BGaWO+FuzZAK4k6gg5QAdGAq+Bxs2Yqx7BzyL7Llp/VfzZeQhCTc/ezuGzKKnKSzpCxXJM8fwNXda3df5RZETlIt6YUzSQDs93sl8w3wBZxCCE10GM1OcWbWjB2mWgEH4Mfdyxm3PSepBHibgQE2wLe7r4HjEidpnXMYdQPKEMJcsZ4zs2POYQOcaPfwMVOo58zsAdMt18BuoVDPxUJRacELbXv3hUIX2vYmOUvi8C8ydz/ThjXrqKqqLbDIAdsCKBd+Wo7GWa7o9qzOQHVVVXeAbs+yHHCH4aTsaCOQqunmUy1yBUAXkdMIfMlgF5EXLo2OpV/c/Up7jG4hhHcYLgWzAZXUc2b2ixsfvc/RmNNfOXD3Q/oeL9axJE1yT9IOoUu6MGUkAAAAAElFTkSuQmCC)
|
|
85
|
+
no-repeat bottom left;
|
|
86
|
+
}
|
|
87
|
+
#full_list li.collapsed a.toggle {
|
|
88
|
+
cursor: default;
|
|
89
|
+
background-position: top left;
|
|
90
|
+
}
|
|
91
|
+
#full_list li.deprecated {
|
|
92
|
+
text-decoration: line-through;
|
|
93
|
+
font-style: italic;
|
|
94
|
+
}
|
|
95
|
+
#full_list li.odd {
|
|
96
|
+
background: #f0f0f0;
|
|
97
|
+
}
|
|
98
|
+
#full_list li.even {
|
|
99
|
+
background: #fafafa;
|
|
100
|
+
}
|
|
101
|
+
#full_list .item:hover {
|
|
102
|
+
background: #ddd;
|
|
103
|
+
}
|
|
104
|
+
#full_list li small:before {
|
|
105
|
+
content: "(";
|
|
106
|
+
}
|
|
107
|
+
#full_list li small:after {
|
|
108
|
+
content: ")";
|
|
109
|
+
}
|
|
110
|
+
#full_list li small.search_info {
|
|
111
|
+
display: none;
|
|
112
|
+
}
|
|
113
|
+
a,
|
|
114
|
+
a:visited {
|
|
115
|
+
text-decoration: none;
|
|
116
|
+
color: #05a;
|
|
117
|
+
}
|
|
118
|
+
#full_list li.clicked > .item {
|
|
119
|
+
background: #05a;
|
|
120
|
+
color: #ccc;
|
|
121
|
+
}
|
|
122
|
+
#full_list li.clicked > .item a,
|
|
123
|
+
#full_list li.clicked > .item a:visited {
|
|
124
|
+
color: #eee;
|
|
125
|
+
}
|
|
126
|
+
#full_list li.clicked > .item a.toggle {
|
|
127
|
+
opacity: 0.5;
|
|
128
|
+
background-position: bottom right;
|
|
129
|
+
}
|
|
130
|
+
#full_list li.collapsed.clicked a.toggle {
|
|
131
|
+
background-position: top right;
|
|
132
|
+
}
|
|
133
|
+
#search input {
|
|
134
|
+
border: 1px solid #bbb;
|
|
135
|
+
border-radius: 3px;
|
|
136
|
+
}
|
|
137
|
+
#full_list_nav {
|
|
138
|
+
margin-left: 10px;
|
|
139
|
+
font-size: 0.9em;
|
|
140
|
+
display: block;
|
|
141
|
+
color: #aaa;
|
|
142
|
+
}
|
|
143
|
+
#full_list_nav a,
|
|
144
|
+
#nav a:visited {
|
|
145
|
+
color: #358;
|
|
146
|
+
}
|
|
147
|
+
#full_list_nav a:hover {
|
|
148
|
+
background: transparent;
|
|
149
|
+
color: #5af;
|
|
150
|
+
}
|
|
151
|
+
#full_list_nav span:after {
|
|
152
|
+
content: " | ";
|
|
153
|
+
}
|
|
154
|
+
#full_list_nav span:last-child:after {
|
|
155
|
+
content: "";
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
#content h1 {
|
|
159
|
+
margin-top: 0;
|
|
160
|
+
}
|
|
161
|
+
#full_list li small {
|
|
162
|
+
display: block;
|
|
163
|
+
font-size: 0.8em;
|
|
164
|
+
}
|
|
165
|
+
#full_list li small:before {
|
|
166
|
+
content: "";
|
|
167
|
+
}
|
|
168
|
+
#full_list li small:after {
|
|
169
|
+
content: "";
|
|
170
|
+
}
|
|
171
|
+
#full_list li small.search_info {
|
|
172
|
+
display: none;
|
|
173
|
+
}
|
|
174
|
+
#search {
|
|
175
|
+
width: 170px;
|
|
176
|
+
position: static;
|
|
177
|
+
margin: 3px;
|
|
178
|
+
margin-left: 10px;
|
|
179
|
+
font-size: 0.9em;
|
|
180
|
+
color: #666;
|
|
181
|
+
padding-left: 0;
|
|
182
|
+
padding-right: 24px;
|
|
183
|
+
}
|
|
184
|
+
#content.insearch #search {
|
|
185
|
+
background-position: center right;
|
|
186
|
+
}
|
|
187
|
+
#search input {
|
|
188
|
+
width: 110px;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
#full_list.insearch ul {
|
|
192
|
+
display: block;
|
|
193
|
+
}
|
|
194
|
+
#full_list.insearch .item {
|
|
195
|
+
display: none;
|
|
196
|
+
}
|
|
197
|
+
#full_list.insearch .found {
|
|
198
|
+
display: block;
|
|
199
|
+
padding-left: 11px;
|
|
200
|
+
}
|
|
201
|
+
#full_list.insearch li a.toggle {
|
|
202
|
+
display: none;
|
|
203
|
+
}
|
|
204
|
+
#full_list.insearch li small.search_info {
|
|
205
|
+
display: block;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* ============================================================
|
|
209
|
+
* parse-stack-next overlay (sidebar / class-list iframe)
|
|
210
|
+
* ============================================================ */
|
|
211
|
+
|
|
212
|
+
:root {
|
|
213
|
+
--ps-bg: #1E1F22;
|
|
214
|
+
--ps-bg-soft: #2A2C30;
|
|
215
|
+
--ps-bg-hover: #34373C;
|
|
216
|
+
--ps-fg: #E6E8EB;
|
|
217
|
+
--ps-fg-muted: #9CA3AF;
|
|
218
|
+
--ps-accent: #E63946;
|
|
219
|
+
--ps-link: #4FB8F4;
|
|
220
|
+
--ps-border: #3A3D42;
|
|
221
|
+
--ps-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Inter",
|
|
222
|
+
Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
body {
|
|
226
|
+
background: var(--ps-bg) !important;
|
|
227
|
+
color: var(--ps-fg);
|
|
228
|
+
font-family: var(--ps-font);
|
|
229
|
+
font-size: 13px;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
h1 {
|
|
233
|
+
color: var(--ps-fg);
|
|
234
|
+
font-weight: 650;
|
|
235
|
+
letter-spacing: -0.01em;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.fixed_header {
|
|
239
|
+
background: var(--ps-bg-soft) !important;
|
|
240
|
+
border-bottom: 1px solid var(--ps-border);
|
|
241
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
#search input,
|
|
245
|
+
#full_list_search input {
|
|
246
|
+
background: var(--ps-bg) !important;
|
|
247
|
+
color: var(--ps-fg) !important;
|
|
248
|
+
border: 1px solid var(--ps-border) !important;
|
|
249
|
+
border-radius: 4px;
|
|
250
|
+
padding: 4px 8px;
|
|
251
|
+
font-family: var(--ps-font);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
#search input::placeholder { color: var(--ps-fg-muted); }
|
|
255
|
+
|
|
256
|
+
#full_list li,
|
|
257
|
+
#full_list li .item {
|
|
258
|
+
color: var(--ps-fg);
|
|
259
|
+
border: 0;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
#full_list li.odd { background: transparent; }
|
|
263
|
+
#full_list li.even { background: rgba(255, 255, 255, .025); }
|
|
264
|
+
|
|
265
|
+
#full_list li a,
|
|
266
|
+
#full_list li .item a,
|
|
267
|
+
#full_list li a:visited,
|
|
268
|
+
#full_list li .item a:visited {
|
|
269
|
+
color: var(--ps-fg);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
#full_list .item:hover,
|
|
273
|
+
#full_list li:hover,
|
|
274
|
+
#full_list li:hover .item {
|
|
275
|
+
background: var(--ps-bg-hover) !important;
|
|
276
|
+
color: var(--ps-fg);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
#full_list li.clicked > .item,
|
|
280
|
+
#full_list li.clicked > .item a,
|
|
281
|
+
#full_list li.clicked > .item a:visited {
|
|
282
|
+
background: var(--ps-accent) !important;
|
|
283
|
+
color: #fff !important;
|
|
284
|
+
border-color: var(--ps-accent) !important;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
#full_list li small { color: var(--ps-fg-muted); }
|
|
288
|
+
#full_list_nav { color: var(--ps-fg-muted); }
|
|
289
|
+
|
|
290
|
+
#full_list_nav a,
|
|
291
|
+
#full_list_nav a:visited { color: var(--ps-link); }
|
|
292
|
+
#full_list_nav a:hover { color: var(--ps-accent); }
|
|
293
|
+
|
|
294
|
+
#noresults {
|
|
295
|
+
background: var(--ps-bg-soft);
|
|
296
|
+
color: var(--ps-fg-muted);
|
|
297
|
+
border-radius: 4px;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
::-webkit-scrollbar { width: 8px; height: 8px; }
|
|
301
|
+
::-webkit-scrollbar-track { background: var(--ps-bg); }
|
|
302
|
+
::-webkit-scrollbar-thumb { background: #4a4d52; border-radius: 4px; }
|
|
303
|
+
::-webkit-scrollbar-thumb:hover { background: var(--ps-link); }
|
|
304
|
+
|
|
305
|
+
/* ============================================================
|
|
306
|
+
* Re-pin iframe-only layout values that common.css would otherwise
|
|
307
|
+
* stomp on.
|
|
308
|
+
*
|
|
309
|
+
* Both stylesheets are loaded inside the class-list iframe AND the
|
|
310
|
+
* main page. common.css generously styles tags like h1, #content,
|
|
311
|
+
* #header for the main page, where their layout role is "big page
|
|
312
|
+
* heading" / "main content column" / "top nav". Inside the iframe
|
|
313
|
+
* those same selectors mean "Class List" / "sidebar viewport" /
|
|
314
|
+
* "fixed top strip", and the sizes need to be drastically smaller.
|
|
315
|
+
* Re-pin them explicitly here so the iframe doesn't inherit the
|
|
316
|
+
* main-page proportions.
|
|
317
|
+
* ============================================================ */
|
|
318
|
+
|
|
319
|
+
#full_list_header,
|
|
320
|
+
body > #content > .fixed_header > h1,
|
|
321
|
+
.fixed_header h1 {
|
|
322
|
+
font-size: 1.4em !important;
|
|
323
|
+
font-weight: 650;
|
|
324
|
+
padding: 12px 10px 4px !important;
|
|
325
|
+
margin: 0 !important;
|
|
326
|
+
border: 0 !important;
|
|
327
|
+
display: block !important;
|
|
328
|
+
color: var(--ps-fg);
|
|
329
|
+
letter-spacing: -0.01em;
|
|
330
|
+
line-height: 1.2;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/* Reserve clearance for the fixed_header. With the larger header
|
|
334
|
+
* font + the flex search row, the stock 80px margin isn't enough. */
|
|
335
|
+
body > #content > #full_list {
|
|
336
|
+
margin-top: 120px !important;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/* The iframe's #content is just a passive scroll container, not the
|
|
340
|
+
* main-page article column. Wipe any main-page color/padding rules. */
|
|
341
|
+
body > #content {
|
|
342
|
+
background: var(--ps-bg) !important;
|
|
343
|
+
color: var(--ps-fg);
|
|
344
|
+
padding: 0;
|
|
345
|
+
margin: 0;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/* Top nav strip: "Classes | Methods | Files" links. */
|
|
349
|
+
#full_list_nav {
|
|
350
|
+
padding: 0 10px;
|
|
351
|
+
font-size: 0.85em;
|
|
352
|
+
color: var(--ps-fg-muted);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/* Search row inside the fixed header — keep it on one line. */
|
|
356
|
+
.fixed_header #search {
|
|
357
|
+
display: flex;
|
|
358
|
+
align-items: center;
|
|
359
|
+
gap: 6px;
|
|
360
|
+
padding: 4px 10px 0;
|
|
361
|
+
width: auto;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.fixed_header #search label {
|
|
365
|
+
color: var(--ps-fg-muted);
|
|
366
|
+
font-size: 0.85em;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.fixed_header #search input {
|
|
370
|
+
flex: 1 1 auto;
|
|
371
|
+
min-width: 0;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/* Belt-and-braces: the fixed strip must be opaque and above the list. */
|
|
375
|
+
.fixed_header {
|
|
376
|
+
background: var(--ps-bg-soft) !important;
|
|
377
|
+
z-index: 9999;
|
|
378
|
+
height: auto;
|
|
379
|
+
min-height: 70px;
|
|
380
|
+
padding-bottom: 8px;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/* The collapse toggle PNG is a dark-on-transparent sprite; invert it
|
|
384
|
+
* so the arrow shows on dark backgrounds. */
|
|
385
|
+
#full_list li a.toggle {
|
|
386
|
+
filter: invert(0.85) hue-rotate(180deg) brightness(1.6);
|
|
387
|
+
}
|
data/lib/parse/agent/tools.rb
CHANGED
|
@@ -469,6 +469,35 @@ module Parse
|
|
|
469
469
|
},
|
|
470
470
|
required: ["class_name", "pipeline"],
|
|
471
471
|
},
|
|
472
|
+
output_schema: {
|
|
473
|
+
type: "object",
|
|
474
|
+
properties: {
|
|
475
|
+
class_name: { type: "string" },
|
|
476
|
+
pipeline_stages: { type: "integer", minimum: 0 },
|
|
477
|
+
result_count: { type: "integer", minimum: 0 },
|
|
478
|
+
# `route` is :mongo_direct or :parse_server but serializes
|
|
479
|
+
# to a Symbol-shaped String in JSON envelopes; declare it
|
|
480
|
+
# permissively as string.
|
|
481
|
+
route: { type: "string", description: "Routing tag: 'mongo_direct' or 'parse_server'." },
|
|
482
|
+
# Aggregation result rows are class-shape-dependent and may
|
|
483
|
+
# be the output of arbitrary $project / $group / $lookup
|
|
484
|
+
# stages. Object envelopes with open property sets are the
|
|
485
|
+
# honest representation.
|
|
486
|
+
results: {
|
|
487
|
+
type: "array",
|
|
488
|
+
items: { type: "object", additionalProperties: true },
|
|
489
|
+
},
|
|
490
|
+
pointer_classes: {
|
|
491
|
+
type: "object",
|
|
492
|
+
additionalProperties: { type: "string" },
|
|
493
|
+
description: "Optional. Field-name → Parse-class-name map when compact_pointers is on.",
|
|
494
|
+
},
|
|
495
|
+
auto_limited: { type: "boolean" },
|
|
496
|
+
auto_limit: { type: "integer", minimum: 1 },
|
|
497
|
+
hint: { type: "string" },
|
|
498
|
+
},
|
|
499
|
+
required: %w[class_name pipeline_stages result_count route results],
|
|
500
|
+
},
|
|
472
501
|
},
|
|
473
502
|
|
|
474
503
|
explain_query: {
|
|
@@ -753,6 +782,25 @@ module Parse
|
|
|
753
782
|
},
|
|
754
783
|
required: ["class_name"],
|
|
755
784
|
},
|
|
785
|
+
output_schema: {
|
|
786
|
+
type: "object",
|
|
787
|
+
properties: {
|
|
788
|
+
class_name: { type: "string" },
|
|
789
|
+
# `format` is one of csv|markdown|table; same shape as the
|
|
790
|
+
# input enum.
|
|
791
|
+
format: { type: "string", enum: %w[csv markdown table] },
|
|
792
|
+
headers: { type: "array", items: { type: "string" } },
|
|
793
|
+
row_count: { type: "integer", minimum: 0 },
|
|
794
|
+
# The serialized output is the formatted CSV / Markdown /
|
|
795
|
+
# text-table string itself — clients render it as-is.
|
|
796
|
+
output: { type: "string" },
|
|
797
|
+
truncated: { type: "boolean" },
|
|
798
|
+
available_rows: { type: "integer", minimum: 0 },
|
|
799
|
+
row_cap: { type: "integer", minimum: 1 },
|
|
800
|
+
hint: { type: "string" },
|
|
801
|
+
},
|
|
802
|
+
required: %w[class_name format headers row_count output],
|
|
803
|
+
},
|
|
756
804
|
},
|
|
757
805
|
|
|
758
806
|
atlas_text_search: {
|
|
@@ -799,6 +847,50 @@ module Parse
|
|
|
799
847
|
},
|
|
800
848
|
required: %w[class_name query],
|
|
801
849
|
},
|
|
850
|
+
output_schema: {
|
|
851
|
+
type: "object",
|
|
852
|
+
properties: {
|
|
853
|
+
class_name: { type: "string" },
|
|
854
|
+
count: { type: "integer", minimum: 0 },
|
|
855
|
+
# Each row is a Parse object projected through the class's
|
|
856
|
+
# agent_fields allowlist, with an Atlas-supplied `score`
|
|
857
|
+
# numeric and an optional `highlights` array when the
|
|
858
|
+
# caller passes highlight_field:. The row shape is class-
|
|
859
|
+
# dependent so additionalProperties is open.
|
|
860
|
+
results: {
|
|
861
|
+
type: "array",
|
|
862
|
+
items: {
|
|
863
|
+
type: "object",
|
|
864
|
+
properties: {
|
|
865
|
+
score: { type: "number" },
|
|
866
|
+
highlights: {
|
|
867
|
+
type: "array",
|
|
868
|
+
items: {
|
|
869
|
+
type: "object",
|
|
870
|
+
properties: {
|
|
871
|
+
path: { type: "string" },
|
|
872
|
+
texts: {
|
|
873
|
+
type: "array",
|
|
874
|
+
items: {
|
|
875
|
+
type: "object",
|
|
876
|
+
properties: {
|
|
877
|
+
value: { type: "string" },
|
|
878
|
+
type: { type: "string", description: "'hit' or 'text' per Atlas spec." },
|
|
879
|
+
},
|
|
880
|
+
required: %w[value],
|
|
881
|
+
},
|
|
882
|
+
},
|
|
883
|
+
},
|
|
884
|
+
required: %w[path],
|
|
885
|
+
},
|
|
886
|
+
},
|
|
887
|
+
},
|
|
888
|
+
additionalProperties: true,
|
|
889
|
+
},
|
|
890
|
+
},
|
|
891
|
+
},
|
|
892
|
+
required: %w[class_name count results],
|
|
893
|
+
},
|
|
802
894
|
},
|
|
803
895
|
|
|
804
896
|
atlas_autocomplete: {
|
|
@@ -838,6 +930,25 @@ module Parse
|
|
|
838
930
|
},
|
|
839
931
|
required: %w[class_name query field],
|
|
840
932
|
},
|
|
933
|
+
output_schema: {
|
|
934
|
+
type: "object",
|
|
935
|
+
properties: {
|
|
936
|
+
class_name: { type: "string" },
|
|
937
|
+
field: { type: "string" },
|
|
938
|
+
# `suggestions` is the list of distinct field values that
|
|
939
|
+
# matched the autocomplete query (deduped, ordered by Atlas
|
|
940
|
+
# ranking). Strings only — autocomplete operates on text.
|
|
941
|
+
suggestions: { type: "array", items: { type: "string" } },
|
|
942
|
+
count: { type: "integer", minimum: 0 },
|
|
943
|
+
# Full matching Parse objects, projected through the class
|
|
944
|
+
# agent_fields allowlist.
|
|
945
|
+
results: {
|
|
946
|
+
type: "array",
|
|
947
|
+
items: { type: "object", additionalProperties: true },
|
|
948
|
+
},
|
|
949
|
+
},
|
|
950
|
+
required: %w[class_name field suggestions count results],
|
|
951
|
+
},
|
|
841
952
|
},
|
|
842
953
|
|
|
843
954
|
atlas_faceted_search: {
|
|
@@ -876,6 +987,40 @@ module Parse
|
|
|
876
987
|
},
|
|
877
988
|
required: %w[class_name facets],
|
|
878
989
|
},
|
|
990
|
+
output_schema: {
|
|
991
|
+
type: "object",
|
|
992
|
+
properties: {
|
|
993
|
+
class_name: { type: "string" },
|
|
994
|
+
# $searchMeta lower-bound count. May be approximate for very
|
|
995
|
+
# large corpora — Atlas documents this; downstream clients
|
|
996
|
+
# should treat it as informative, not a precise total.
|
|
997
|
+
total_count: { type: "integer", minimum: 0 },
|
|
998
|
+
# Facets is a Map<facet_name, { buckets: [{_id, count}] }>.
|
|
999
|
+
# Bucket _id is heterogeneous (String for string facets,
|
|
1000
|
+
# Number/Date for numeric/date facets), so additionalProperties:true
|
|
1001
|
+
# on the bucket entry keeps the contract honest without
|
|
1002
|
+
# bloating the schema with per-type variants.
|
|
1003
|
+
facets: {
|
|
1004
|
+
type: "object",
|
|
1005
|
+
additionalProperties: {
|
|
1006
|
+
type: "object",
|
|
1007
|
+
properties: {
|
|
1008
|
+
buckets: {
|
|
1009
|
+
type: "array",
|
|
1010
|
+
items: { type: "object", additionalProperties: true },
|
|
1011
|
+
},
|
|
1012
|
+
},
|
|
1013
|
+
required: %w[buckets],
|
|
1014
|
+
},
|
|
1015
|
+
},
|
|
1016
|
+
count: { type: "integer", minimum: 0 },
|
|
1017
|
+
results: {
|
|
1018
|
+
type: "array",
|
|
1019
|
+
items: { type: "object", additionalProperties: true },
|
|
1020
|
+
},
|
|
1021
|
+
},
|
|
1022
|
+
required: %w[class_name total_count facets count results],
|
|
1023
|
+
},
|
|
879
1024
|
},
|
|
880
1025
|
}.freeze
|
|
881
1026
|
|
|
@@ -3474,7 +3619,14 @@ module Parse
|
|
|
3474
3619
|
class_name: class_name,
|
|
3475
3620
|
pipeline_stages: pipeline.size,
|
|
3476
3621
|
result_count: results.size,
|
|
3477
|
-
|
|
3622
|
+
# Coerce to String here so the value lands in
|
|
3623
|
+
# `structuredContent` as a String (matching the
|
|
3624
|
+
# advertised output_schema `type: "string"`). Without the
|
|
3625
|
+
# `.to_s`, MCP clients validating structuredContent see a
|
|
3626
|
+
# Ruby Symbol pre-serialization and fail the type check;
|
|
3627
|
+
# downstream JSON serialization would convert it but the
|
|
3628
|
+
# client-side validator runs before that.
|
|
3629
|
+
route: (use_mongo_direct ? :mongo_direct : :parse_server).to_s,
|
|
3478
3630
|
results: results,
|
|
3479
3631
|
}
|
|
3480
3632
|
result[:pointer_classes] = pointer_map if pointer_map.any?
|
data/lib/parse/cache/redis.rb
CHANGED
|
@@ -118,6 +118,59 @@ module Parse
|
|
|
118
118
|
@pool.increment(key, amount, options)
|
|
119
119
|
end
|
|
120
120
|
|
|
121
|
+
# Lua compare-and-delete: delete `key` only if its current value
|
|
122
|
+
# equals `expected`. Atomic on the Redis server (the GET, the
|
|
123
|
+
# compare, and the DEL are one script invocation), which closes the
|
|
124
|
+
# check-then-delete race in a naive GET-then-DEL release where the
|
|
125
|
+
# lease can expire and be re-acquired by another holder between the
|
|
126
|
+
# two commands.
|
|
127
|
+
LOCK_RELEASE_SCRIPT = <<~LUA
|
|
128
|
+
if redis.call('get', KEYS[1]) == ARGV[1] then
|
|
129
|
+
return redis.call('del', KEYS[1])
|
|
130
|
+
else
|
|
131
|
+
return 0
|
|
132
|
+
end
|
|
133
|
+
LUA
|
|
134
|
+
|
|
135
|
+
# Atomically acquire a lock: SET key=owner only if absent, with a
|
|
136
|
+
# native expiry. Used by {Parse::LockBackend} for {Parse::Lock} and
|
|
137
|
+
# {Parse::CreateLock}. Deliberately bypasses Moneta's `create` —
|
|
138
|
+
# `Moneta.new(:Redis)` marshals BOTH keys and values, so a raw-Redis
|
|
139
|
+
# compare-and-delete on the marshaled blob would be fragile and
|
|
140
|
+
# coupled to Moneta's serializer config. Routing acquire AND release
|
|
141
|
+
# through plain-string raw Redis here keeps one consistent encoding
|
|
142
|
+
# across both ends of the lock and makes the keys human-inspectable
|
|
143
|
+
# in Redis (`parse-stack:lock:v1:<digest>`). Lock keys are
|
|
144
|
+
# short-lived (TTL ≤ 30s) so there is no migration concern when a
|
|
145
|
+
# deploy flips between the Moneta-encoded and raw-encoded paths.
|
|
146
|
+
#
|
|
147
|
+
# @param key [String] plain-string lock key.
|
|
148
|
+
# @param owner [String] unique-per-acquisition owner token.
|
|
149
|
+
# @param ttl [Integer] seconds until the key self-clears.
|
|
150
|
+
# @return [Boolean] true when the key was set (lock acquired).
|
|
151
|
+
def lock_acquire(key, owner, ttl)
|
|
152
|
+
@pool.pool.with do |store|
|
|
153
|
+
redis = backend_client(store)
|
|
154
|
+
# redis-rb returns "OK" on success, nil when NX fails.
|
|
155
|
+
!!redis.set(key, owner, nx: true, ex: ttl)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Atomically release a lock via compare-and-delete. Only the holder
|
|
160
|
+
# whose `owner` token still matches the stored value deletes the
|
|
161
|
+
# key — a holder whose lease already expired and was re-acquired by
|
|
162
|
+
# someone else is a no-op, never a cross-holder delete.
|
|
163
|
+
#
|
|
164
|
+
# @param key [String] plain-string lock key.
|
|
165
|
+
# @param owner [String] the owner token from {#lock_acquire}.
|
|
166
|
+
# @return [Boolean] true when this owner's key was deleted.
|
|
167
|
+
def lock_release(key, owner)
|
|
168
|
+
@pool.pool.with do |store|
|
|
169
|
+
redis = backend_client(store)
|
|
170
|
+
redis.eval(LOCK_RELEASE_SCRIPT, keys: [key], argv: [owner]).to_i == 1
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
121
174
|
# Clear cached entries belonging to this wrapper. Required for
|
|
122
175
|
# `Parse::Client#clear_cache!` compatibility.
|
|
123
176
|
#
|
data/lib/parse/client/caching.rb
CHANGED
|
@@ -143,6 +143,19 @@ module Parse
|
|
|
143
143
|
@cache_key = "mk:#{@cache_key}" # prefix for master key requests
|
|
144
144
|
end
|
|
145
145
|
|
|
146
|
+
# Optional ambient cache-tenant scope from `Parse.with_cache_tenant`.
|
|
147
|
+
# When present, composes between the configured namespace and the
|
|
148
|
+
# token/mk prefix as `T:<tenant>:` so a SCAN-delete over
|
|
149
|
+
# `<namespace>:T:<tenant>:*` evicts exactly one tenant, and
|
|
150
|
+
# `<namespace>:*` still evicts the whole namespace cleanly. The
|
|
151
|
+
# `T:` discriminator makes tenant prefixes unambiguously
|
|
152
|
+
# distinguishable from session-token hex prefixes (32-char hex)
|
|
153
|
+
# and from `mk:`, so legacy cache entries written before the
|
|
154
|
+
# tenant feature don't accidentally re-hydrate into a tenanted
|
|
155
|
+
# request and vice versa.
|
|
156
|
+
@cache_tenant = Parse.respond_to?(:current_cache_tenant) ? Parse.current_cache_tenant : nil
|
|
157
|
+
@cache_key = "T:#{@cache_tenant}:#{@cache_key}" if @cache_tenant
|
|
158
|
+
|
|
146
159
|
# Namespace outermost so a SCAN over `<namespace>:*` evicts a whole
|
|
147
160
|
# tenant/app cleanly without touching another app's entries.
|
|
148
161
|
@cache_key = "#{@namespace}:#{@cache_key}" if @namespace
|
|
@@ -277,7 +290,11 @@ module Parse
|
|
|
277
290
|
# @!visibility private
|
|
278
291
|
def instrument_cache(event, **extra)
|
|
279
292
|
return unless defined?(ActiveSupport::Notifications)
|
|
280
|
-
payload = {
|
|
293
|
+
payload = {
|
|
294
|
+
event: event,
|
|
295
|
+
namespace: @namespace,
|
|
296
|
+
cache_tenant: @cache_tenant,
|
|
297
|
+
}.merge!(extra)
|
|
281
298
|
ActiveSupport::Notifications.instrument("parse.cache.#{event}", payload)
|
|
282
299
|
end
|
|
283
300
|
|