parse-stack-next 5.7.6 → 5.8.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/CHANGELOG.md +830 -0
- data/README.md +14 -4
- data/docs/TEST_SERVER.md +2 -2
- data/docs/acl_clp_guide.md +7 -0
- data/docs/atlas_vector_search_guide.md +181 -13
- data/docs/client_sdk_guide.md +11 -0
- data/docs/mcp_guide.md +317 -6
- data/docs/mongodb_direct_guide.md +27 -0
- data/docs/usage_guide.md +38 -0
- data/docs/webhooks_guide.md +74 -17
- data/lib/parse/acl_scope.rb +159 -41
- data/lib/parse/agent/approval_gate.rb +0 -0
- data/lib/parse/agent/constraint_translator.rb +42 -15
- data/lib/parse/agent/describe.rb +3 -1
- data/lib/parse/agent/field_names.rb +53 -0
- data/lib/parse/agent/field_policy.rb +74 -0
- data/lib/parse/agent/mcp_deployments.rb +426 -0
- data/lib/parse/agent/mcp_rack_app.rb +424 -45
- data/lib/parse/agent/mcp_server.rb +23 -1
- data/lib/parse/agent/mcp_subscriptions.rb +124 -6
- data/lib/parse/agent/metadata_registry.rb +67 -8
- data/lib/parse/agent/prompt_hardening.rb +9 -3
- data/lib/parse/agent/tools.rb +378 -29
- data/lib/parse/agent.rb +93 -1
- data/lib/parse/api/batch.rb +10 -1
- data/lib/parse/api/schema.rb +23 -4
- data/lib/parse/api/sessions.rb +6 -2
- data/lib/parse/api/users.rb +88 -14
- data/lib/parse/atlas_search/protected_paths.rb +236 -0
- data/lib/parse/atlas_search.rb +95 -23
- data/lib/parse/authorization.rb +54 -1
- data/lib/parse/client/batch.rb +231 -35
- data/lib/parse/client/body_builder.rb +21 -0
- data/lib/parse/client/caching.rb +371 -27
- data/lib/parse/client/request.rb +26 -14
- data/lib/parse/client/response.rb +49 -6
- data/lib/parse/client.rb +201 -38
- data/lib/parse/clp_scope.rb +281 -23
- data/lib/parse/console.rb +2 -2
- data/lib/parse/embeddings/voyage.rb +181 -17
- data/lib/parse/graphql/type_generator.rb +3 -0
- data/lib/parse/model/acl.rb +119 -21
- data/lib/parse/model/associations/belongs_to.rb +25 -3
- data/lib/parse/model/associations/collection_proxy.rb +138 -17
- data/lib/parse/model/associations/has_many.rb +38 -9
- data/lib/parse/model/associations/has_one.rb +3 -1
- data/lib/parse/model/associations/pointer_collection_proxy.rb +109 -17
- data/lib/parse/model/associations/relation_collection_proxy.rb +134 -28
- data/lib/parse/model/bytes.rb +13 -5
- data/lib/parse/model/classes/role.rb +72 -0
- data/lib/parse/model/classes/session.rb +43 -0
- data/lib/parse/model/classes/user.rb +78 -3
- data/lib/parse/model/core/actions.rb +269 -67
- data/lib/parse/model/core/builder.rb +100 -8
- data/lib/parse/model/core/create_lock.rb +27 -2
- data/lib/parse/model/core/describe.rb +2 -0
- data/lib/parse/model/core/fetching.rb +21 -3
- data/lib/parse/model/core/pluralized_aliases.rb +8 -4
- data/lib/parse/model/core/properties.rb +488 -39
- data/lib/parse/model/core/querying.rb +7 -0
- data/lib/parse/model/core/schema.rb +5 -3
- data/lib/parse/model/core/search_indexing.rb +63 -0
- data/lib/parse/model/core/vector_searchable.rb +35 -6
- data/lib/parse/model/file.rb +9 -2
- data/lib/parse/model/geopoint.rb +61 -13
- data/lib/parse/model/model.rb +160 -9
- data/lib/parse/model/object.rb +265 -17
- data/lib/parse/model/phone.rb +54 -5
- data/lib/parse/model/pointer.rb +40 -6
- data/lib/parse/mongodb.rb +170 -60
- data/lib/parse/pipeline_security.rb +415 -26
- data/lib/parse/query/constraint.rb +30 -0
- data/lib/parse/query/constraints.rb +58 -32
- data/lib/parse/query/cursor.rb +3 -1
- data/lib/parse/query/operation.rb +62 -8
- data/lib/parse/query/ordering.rb +34 -6
- data/lib/parse/query.rb +1100 -134
- data/lib/parse/retrieval/agent_tool.rb +225 -8
- data/lib/parse/retrieval/benchmark.rb +149 -0
- data/lib/parse/retrieval/profiles.rb +320 -0
- data/lib/parse/retrieval/retriever.rb +10 -1
- data/lib/parse/retrieval.rb +2 -0
- data/lib/parse/schema/search_index_migrator.rb +23 -5
- data/lib/parse/schema.rb +74 -18
- data/lib/parse/stack/tasks.rb +6 -4
- data/lib/parse/stack/version.rb +1 -1
- data/lib/parse/stack.rb +72 -14
- data/lib/parse/two_factor_auth/user_extension.rb +14 -2
- data/lib/parse/two_factor_auth.rb +11 -0
- data/lib/parse/vector_search/hybrid.rb +36 -18
- data/lib/parse/vector_search/index_definition.rb +237 -0
- data/lib/parse/vector_search.rb +46 -17
- data/lib/parse/webhooks/payload.rb +93 -6
- data/lib/parse/webhooks/replay_protection.rb +58 -20
- data/lib/parse/webhooks.rb +412 -40
- metadata +8 -1
|
@@ -34,11 +34,18 @@ module Parse
|
|
|
34
34
|
# material against snapshot/MONITOR exposure should set
|
|
35
35
|
# `PARSE_STACK_LOCK_SECRET` or `Parse.synchronize_create_secret`.
|
|
36
36
|
module CreateLock
|
|
37
|
+
# Floor for the lock lease, in seconds. When no `ttl:` is given the
|
|
38
|
+
# lease is sized from the client's request budget instead (see
|
|
39
|
+
# {.default_ttl}); this is the minimum it can be.
|
|
37
40
|
DEFAULT_TTL = 3
|
|
38
41
|
DEFAULT_WAIT = 2.0
|
|
39
42
|
DEFAULT_POLL_BASE = 0.05
|
|
40
43
|
DEFAULT_POLL_JITTER = 0.015
|
|
41
|
-
|
|
44
|
+
# Ceiling for the lock lease, in seconds. The lease only matters when a
|
|
45
|
+
# holder dies without releasing; a normal exit always releases.
|
|
46
|
+
MAX_TTL = 300
|
|
47
|
+
# Requests the locked block is sized for: the find, then the create.
|
|
48
|
+
LOCKED_REQUESTS = 2
|
|
42
49
|
MAX_WAIT = 30
|
|
43
50
|
MAX_PAYLOAD_BYTES = 8_192
|
|
44
51
|
MAX_DEPTH = 4
|
|
@@ -60,7 +67,7 @@ module Parse
|
|
|
60
67
|
def synchronize(parse_class:, query_attrs:, options: {}, session_token: nil, master_key: nil, &block)
|
|
61
68
|
raise ArgumentError, "block required" unless block_given?
|
|
62
69
|
|
|
63
|
-
ttl = clamp(Integer(options[:ttl]
|
|
70
|
+
ttl = options[:ttl] ? clamp(Integer(options[:ttl]), 1, MAX_TTL) : default_ttl
|
|
64
71
|
wait = clamp(Float(options[:wait] || DEFAULT_WAIT), 0.0, MAX_WAIT)
|
|
65
72
|
on_degraded = options[:on_degraded] || :warn
|
|
66
73
|
|
|
@@ -115,6 +122,24 @@ module Parse
|
|
|
115
122
|
end
|
|
116
123
|
end
|
|
117
124
|
|
|
125
|
+
# The lease used when no `ttl:` is given. The lock is not renewed
|
|
126
|
+
# while held, so the lease must outlast the block it guards: a find
|
|
127
|
+
# and a create, each of which may run through every retry at the full
|
|
128
|
+
# request timeout. A lease shorter than that expires mid-block, lets a
|
|
129
|
+
# second caller in, and produces the duplicate the lock exists to
|
|
130
|
+
# prevent. The result is clamped to DEFAULT_TTL..MAX_TTL. Pass `ttl:`
|
|
131
|
+
# to override it.
|
|
132
|
+
# @param client [Parse::Client, nil] the client whose timeouts and
|
|
133
|
+
# retry limit size the lease; the default client when nil.
|
|
134
|
+
# @return [Integer] seconds.
|
|
135
|
+
def default_ttl(client = nil)
|
|
136
|
+
client ||= Parse::Client.client
|
|
137
|
+
budget = client.respond_to?(:request_time_budget) ? client.request_time_budget : 0
|
|
138
|
+
clamp((budget * LOCKED_REQUESTS).ceil, DEFAULT_TTL, MAX_TTL)
|
|
139
|
+
rescue StandardError
|
|
140
|
+
DEFAULT_TTL
|
|
141
|
+
end
|
|
142
|
+
|
|
118
143
|
# Canonical lock key for the given inputs. Public for tests.
|
|
119
144
|
# @return [String]
|
|
120
145
|
def canonical_key(parse_class:, query_attrs:, session_token: nil, master_key: nil)
|
|
@@ -145,6 +145,8 @@ module Parse
|
|
|
145
145
|
unless defined?(Parse::MongoDB) && Parse::MongoDB.respond_to?(:enabled?) && Parse::MongoDB.enabled?
|
|
146
146
|
return { available: false, reason: :mongodb_not_enabled }
|
|
147
147
|
end
|
|
148
|
+
# Parse::AtlasSearch is loaded on first use, not with the SDK.
|
|
149
|
+
require_relative "../../atlas_search"
|
|
148
150
|
indexes = Parse::AtlasSearch::IndexManager.list_indexes(parse_class)
|
|
149
151
|
{
|
|
150
152
|
available: true,
|
|
@@ -87,7 +87,11 @@ module Parse
|
|
|
87
87
|
includes_array = includes.present? ? Array(includes) : nil
|
|
88
88
|
|
|
89
89
|
# Build formatted keys once (reused for query and tracking)
|
|
90
|
-
formatted_keys = keys_array
|
|
90
|
+
formatted_keys = if keys_array
|
|
91
|
+
Parse::Query.with_field_aliases(parse_class) do
|
|
92
|
+
keys_array.map { |k| Parse::Query.format_field(k) }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
91
95
|
|
|
92
96
|
# Validate keys against model fields if validation is enabled
|
|
93
97
|
# Skip validation if warnings are disabled (nothing to report)
|
|
@@ -203,6 +207,15 @@ module Parse
|
|
|
203
207
|
|
|
204
208
|
# Apply attributes from server (only keys in result get updated)
|
|
205
209
|
apply_attributes!(result, dirty_track: false)
|
|
210
|
+
# The server row is now the source of truth for the ACL. A full
|
|
211
|
+
# fetch whose response has no ACL key is a public (ACL-less) row, so
|
|
212
|
+
# drop any locally stamped default rather than misreport it. Either
|
|
213
|
+
# way the record exists, so the save-time default-ACL resolver must
|
|
214
|
+
# not replace its ACL.
|
|
215
|
+
if !is_partial_fetch && !result.key?("ACL") && !result.key?(:ACL)
|
|
216
|
+
@acl = nil
|
|
217
|
+
end
|
|
218
|
+
@_acl_pristine = false
|
|
206
219
|
record_authorization_hydration!(result, partial: is_partial_fetch) if
|
|
207
220
|
respond_to?(:record_authorization_hydration!)
|
|
208
221
|
|
|
@@ -318,7 +331,9 @@ module Parse
|
|
|
318
331
|
def fetch_json(keys: nil, includes: nil)
|
|
319
332
|
query = {}
|
|
320
333
|
if keys.present?
|
|
321
|
-
keys_array =
|
|
334
|
+
keys_array = Parse::Query.with_field_aliases(parse_class) do
|
|
335
|
+
Array(keys).map { |k| Parse::Query.format_field(k) }
|
|
336
|
+
end
|
|
322
337
|
query[:keys] = keys_array.join(",")
|
|
323
338
|
end
|
|
324
339
|
if includes.present?
|
|
@@ -432,9 +447,12 @@ module Parse
|
|
|
432
447
|
# Autofetch if object is a pointer OR was selectively fetched
|
|
433
448
|
# Skip if autofetch is disabled for this instance
|
|
434
449
|
needs_fetch = pointer? || has_selective_keys?
|
|
450
|
+
# A pointer never autofetches for its ACL. A partially fetched object
|
|
451
|
+
# does when the ACL was left out, so code that edits it starts from
|
|
452
|
+
# the record's real ACL instead of nil.
|
|
435
453
|
return unless needs_fetch &&
|
|
436
454
|
!autofetch_disabled? &&
|
|
437
|
-
key
|
|
455
|
+
!(key == :acl && pointer?) &&
|
|
438
456
|
!Parse::Properties::BASE_KEYS.include?(key) &&
|
|
439
457
|
respond_to?(:fetch)
|
|
440
458
|
|
|
@@ -4,12 +4,16 @@
|
|
|
4
4
|
module Parse
|
|
5
5
|
# Global `const_missing` hook that lazily resolves the plural form of a
|
|
6
6
|
# {Parse::Object} subclass constant to that class. Referencing `Posts`
|
|
7
|
-
# when a class `Post` exists installs `Posts` as an alias for `Post`
|
|
8
|
-
#
|
|
7
|
+
# when a class `Post` exists installs `Posts` as an alias for `Post` and
|
|
8
|
+
# returns it, so query entry points like
|
|
9
9
|
# `Posts.where(...).count` work without any per-model boilerplate.
|
|
10
10
|
#
|
|
11
|
-
# The hook is prepended onto `Module` so
|
|
12
|
-
#
|
|
11
|
+
# The hook is prepended onto `Module` so a plural reference resolves from
|
|
12
|
+
# any namespace, but the alias constant is only ever installed in the
|
|
13
|
+
# namespace that defines the singular class (`::Posts` for a top-level
|
|
14
|
+
# `Post`, `Blog::Posts` for `Blog::Post`). A lookup from an unrelated
|
|
15
|
+
# module never adds a constant to that module, and a frozen namespace is
|
|
16
|
+
# skipped rather than raising `FrozenError`. It is tightly guarded: every
|
|
13
17
|
# path that is not a plural-of-a-Parse-class falls through to `super`,
|
|
14
18
|
# preserving normal `NameError` and autoloader (Zeitwerk/classic)
|
|
15
19
|
# behavior. The whole feature is gated on {Parse.pluralized_aliases?} so
|