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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.yml +105 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.yml +67 -0
  4. data/.github/dependabot.yml +13 -0
  5. data/.github/workflows/codeql.yml +1 -1
  6. data/.github/workflows/docs.yml +3 -3
  7. data/.github/workflows/release.yml +14 -3
  8. data/.github/workflows/ruby.yml +1 -1
  9. data/.gitignore +1 -0
  10. data/.yardopts +19 -0
  11. data/CHANGELOG.md +792 -0
  12. data/Gemfile +3 -0
  13. data/Gemfile.lock +8 -5
  14. data/README.md +15 -0
  15. data/Rakefile +5 -1
  16. data/docs/acl_clp_guide.md +553 -0
  17. data/docs/atlas_vector_search_guide.md +123 -22
  18. data/docs/client_sdk_guide.md +201 -5
  19. data/docs/usage_guide.md +21 -0
  20. data/docs/yard-template/default/fulldoc/html/css/common.css +1222 -0
  21. data/docs/yard-template/default/fulldoc/html/css/full_list.css +387 -0
  22. data/lib/parse/agent/tools.rb +153 -1
  23. data/lib/parse/cache/redis.rb +53 -0
  24. data/lib/parse/client/caching.rb +18 -1
  25. data/lib/parse/client.rb +79 -12
  26. data/lib/parse/embeddings/cohere.rb +143 -6
  27. data/lib/parse/embeddings/provider.rb +20 -2
  28. data/lib/parse/embeddings/voyage.rb +102 -0
  29. data/lib/parse/embeddings.rb +332 -1
  30. data/lib/parse/live_query/client.rb +167 -4
  31. data/lib/parse/live_query/configuration.rb +12 -0
  32. data/lib/parse/live_query/subscription.rb +55 -2
  33. data/lib/parse/live_query.rb +123 -1
  34. data/lib/parse/lock.rb +342 -0
  35. data/lib/parse/lock_backend.rb +308 -0
  36. data/lib/parse/model/classes/audience.rb +5 -0
  37. data/lib/parse/model/classes/installation.rb +122 -0
  38. data/lib/parse/model/classes/job_schedule.rb +3 -1
  39. data/lib/parse/model/classes/job_status.rb +4 -1
  40. data/lib/parse/model/classes/push_status.rb +4 -1
  41. data/lib/parse/model/classes/session.rb +7 -0
  42. data/lib/parse/model/classes/user.rb +204 -0
  43. data/lib/parse/model/core/create_lock.rb +28 -146
  44. data/lib/parse/model/core/embed_managed.rb +162 -13
  45. data/lib/parse/model/core/parse_reference.rb +17 -1
  46. data/lib/parse/model/core/querying.rb +26 -2
  47. data/lib/parse/model/file.rb +523 -18
  48. data/lib/parse/query.rb +31 -1
  49. data/lib/parse/stack/version.rb +1 -1
  50. data/lib/parse/stack.rb +98 -1
  51. data/parse-stack-next.gemspec +2 -2
  52. metadata +17 -7
data/lib/parse/query.rb CHANGED
@@ -2966,9 +2966,26 @@ module Parse
2966
2966
  # @param fields [Array<String>] specific fields to watch for changes (nil = all fields)
2967
2967
  # @param session_token [String] session token for ACL-aware subscriptions
2968
2968
  # @param client [Parse::LiveQuery::Client] custom LiveQuery client (optional)
2969
+ # @param use_master_key [Boolean] an intent assertion, NOT a
2970
+ # per-subscription elevation. Parse Server resolves `masterKey`
2971
+ # once, at connect time, from the LiveQuery connect frame; the
2972
+ # subscribe frame never carries it. This flag therefore only has
2973
+ # effect when the underlying client is itself an admin connection
2974
+ # (`Parse::LiveQuery::Client.new(use_master_key: true)` with a
2975
+ # master key), in which case the entire socket is already elevated
2976
+ # and ALL its subscriptions bypass ACL/CLP. On a non-admin
2977
+ # connection `use_master_key: true` does not elevate the
2978
+ # subscription and emits a security warning. A single socket cannot
2979
+ # mix scoped and admin subscriptions — use separate connections for
2980
+ # end-user (session-token-scoped) versus administrative
2981
+ # (master-key-scoped) work.
2982
+ # @yield [subscription] runs the block with the freshly-constructed
2983
+ # {Parse::LiveQuery::Subscription} BEFORE the subscribe frame is
2984
+ # sent so caller-registered callbacks are wired before any server
2985
+ # events can arrive. Optional.
2969
2986
  # @return [Parse::LiveQuery::Subscription] the subscription object
2970
2987
  # @see Parse::LiveQuery::Subscription
2971
- def subscribe(fields: nil, session_token: nil, client: nil)
2988
+ def subscribe(fields: nil, session_token: nil, client: nil, use_master_key: false, &block)
2972
2989
  require_relative "live_query"
2973
2990
 
2974
2991
  lq_client = client || Parse::LiveQuery.client
@@ -2977,6 +2994,8 @@ module Parse
2977
2994
  where: compile_where,
2978
2995
  fields: fields,
2979
2996
  session_token: session_token || @session_token,
2997
+ use_master_key: use_master_key,
2998
+ &block
2980
2999
  )
2981
3000
  end
2982
3001
 
@@ -5420,6 +5439,17 @@ module Parse
5420
5439
  # Helper class for executing arbitrary MongoDB aggregation pipelines.
5421
5440
  # Provides a consistent interface with results, raw, and result_pointers methods.
5422
5441
  class Aggregation
5442
+ # @return [Array<Hash>] the MongoDB aggregation pipeline stages this
5443
+ # Aggregation will execute. Useful for previewing the routed pipeline
5444
+ # before {#execute!}, for snapshot-based regression tests, and for
5445
+ # debugging the REST-vs-mongo-direct translation.
5446
+ attr_reader :pipeline
5447
+
5448
+ # @return [Boolean] whether {#execute!} will route through
5449
+ # {Parse::MongoDB.aggregate} instead of Parse Server's REST
5450
+ # `/aggregate` endpoint.
5451
+ attr_reader :mongo_direct
5452
+
5423
5453
  # @param query [Parse::Query] the base query object
5424
5454
  # @param pipeline [Array<Hash>] the MongoDB aggregation pipeline stages
5425
5455
  # @param verbose [Boolean, nil] whether to print verbose output (nil means use query's setting)
@@ -6,6 +6,6 @@ module Parse
6
6
  # The Parse Server SDK for Ruby
7
7
  module Stack
8
8
  # The current version.
9
- VERSION = "5.0.1"
9
+ VERSION = "5.1.0"
10
10
  end
11
11
  end
data/lib/parse/stack.rb CHANGED
@@ -154,6 +154,81 @@ module Parse
154
154
  Fiber[SESSION_TOKEN_STATE_KEY]
155
155
  end
156
156
 
157
+ # @!visibility private
158
+ CACHE_TENANT_STATE_KEY = :__parse_cache_tenant__
159
+
160
+ # Set an ambient cache-tenant scope for the duration of the block.
161
+ # When set, the {Parse::Middleware::Caching} middleware composes the
162
+ # tenant into the cache key as `<base-namespace>:T:<tenant>:…` so a
163
+ # multi-tenant Parse application can share one Redis (or any Moneta-
164
+ # backed cache) without per-tenant configuration plumbing through
165
+ # every `Parse::Client.new` site. Tenants do not see each other's
166
+ # cached responses; a SCAN-delete over `<base-namespace>:T:<tenant>:*`
167
+ # evicts exactly one tenant cleanly.
168
+ #
169
+ # This is purely a key namespacing mechanism — it does NOT enforce
170
+ # any access-control semantics. Tenant isolation at the data layer
171
+ # is the job of `agent_tenant_scope` (per-class scoping) and ACL/CLP.
172
+ # The tenant cache scope's role is to keep tenant A's session-token-
173
+ # keyed cache entry from being served on tenant B's request even
174
+ # when the URL and session token happen to collide.
175
+ #
176
+ # Fiber-local — composes safely with `async` and concurrent web
177
+ # frameworks. The scope is per-fiber, not per-thread, and is
178
+ # restored on block exit even if the block raises.
179
+ #
180
+ # @example wrap an agent invocation under a tenant
181
+ # Parse.with_cache_tenant("tenant_abc") do
182
+ # agent.run(prompt) # every Parse request issued inside the
183
+ # # block writes/reads tenant-scoped cache
184
+ # # entries
185
+ # end
186
+ #
187
+ # @example compose with `with_session`
188
+ # Parse.with_cache_tenant(tenant_id) do
189
+ # Parse.with_session(user) do
190
+ # Post.all
191
+ # end
192
+ # end
193
+ #
194
+ # @param scope [String, Symbol, nil] tenant identifier. `nil` clears
195
+ # the ambient scope for the duration of the block (useful to opt
196
+ # out within a larger tenant-scoped section). Must be ASCII
197
+ # `[A-Za-z0-9_-]+` — colon and other key-segment-delimiter chars
198
+ # are refused with `ArgumentError`, since the middleware composes
199
+ # the tenant into the cache key as `T:<tenant>:…` and a tenant
200
+ # containing `:` would collapse the segmentation (e.g.
201
+ # `with_cache_tenant("a:T:b")` would produce keys
202
+ # indistinguishable from `with_cache_tenant("a")` nested under
203
+ # `with_cache_tenant("b")`, breaking SCAN-delete isolation).
204
+ # @yield runs the block with the ambient tenant scope in place
205
+ # @return [Object] the block's return value
206
+ # @raise [ArgumentError] when `scope` contains characters outside
207
+ # `[A-Za-z0-9_-]` or exceeds 256 bytes.
208
+ CACHE_TENANT_PATTERN = /\A[A-Za-z0-9_\-]{1,256}\z/.freeze
209
+ def self.with_cache_tenant(scope)
210
+ resolved = scope.nil? ? nil : scope.to_s
211
+ resolved = nil if resolved&.empty?
212
+ if resolved && !CACHE_TENANT_PATTERN.match?(resolved)
213
+ raise ArgumentError,
214
+ "Parse.with_cache_tenant scope must match #{CACHE_TENANT_PATTERN.source} " \
215
+ "(got #{scope.inspect}). Colon and other key-segment-delimiter characters " \
216
+ "are refused — they would collapse the cache-key namespace boundary."
217
+ end
218
+ previous = Fiber[CACHE_TENANT_STATE_KEY]
219
+ Fiber[CACHE_TENANT_STATE_KEY] = resolved
220
+ yield
221
+ ensure
222
+ Fiber[CACHE_TENANT_STATE_KEY] = previous
223
+ end
224
+
225
+ # The ambient cache-tenant scope set by {.with_cache_tenant} for the
226
+ # current fiber, or `nil` when not inside such a block.
227
+ # @return [String, nil]
228
+ def self.current_cache_tenant
229
+ Fiber[CACHE_TENANT_STATE_KEY]
230
+ end
231
+
157
232
  # The {Parse::User} cached alongside the ambient session set by
158
233
  # {.login}, or `nil` when no imperative login is active. Block-scoped
159
234
  # `{Parse.with_session}` does NOT populate this — only {.login} does.
@@ -336,9 +411,31 @@ module Parse
336
411
  # so the network-egress surface (an outbound WebSocket to the LiveQuery
337
412
  # server) is opened only when the operator explicitly turns it on, not
338
413
  # as a side effect of requiring the file.
414
+ #
415
+ # The LiveQuery module is autoloaded — `Parse::LiveQuery.configure { … }`
416
+ # works without an explicit `require 'parse/live_query'`. The autoload
417
+ # is purely a file-loading convenience; it does NOT open a network
418
+ # connection. A connection only opens when `Parse.live_query_enabled = true`
419
+ # AND a `Parse::LiveQuery::Client` is instantiated (typically via
420
+ # `Klass.subscribe { … }` or `Parse::Client.new(live_query_url: …)`).
421
+ #
339
422
  # @example Enable LiveQuery
340
423
  # Parse.live_query_enabled = true
341
- # require 'parse/live_query'
424
+ # # Parse::LiveQuery is autoloaded — no explicit require needed
425
+ # Parse::LiveQuery.configure do |c|
426
+ # c.url = "wss://parse.example.com"
427
+ # end
428
+ autoload :LiveQuery, "parse/live_query"
429
+
430
+ # Public mutual-exclusion primitive (TTL-bounded, Redis-backed with
431
+ # in-process Mutex fallback). See {Parse::Lock}.
432
+ autoload :Lock, "parse/lock"
433
+
434
+ # Shared low-level lock primitives consumed by both {Parse::Lock}
435
+ # and {Parse::CreateLock}. `@api private` — application code should
436
+ # use {Parse::Lock.acquire}.
437
+ autoload :LockBackend, "parse/lock_backend"
438
+
342
439
  @live_query_enabled = false
343
440
 
344
441
  # Configuration for cache write-through on fetch operations.
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Anthony Persaud", "Henry Spindell", "Adrian Curtin"]
10
10
  spec.email = ["adrian+parse-stack@neurosynq.net"]
11
11
 
12
- spec.summary = %q{Parse Server Ruby Client SDK (parse-stack-next fork)}
13
- spec.description = %q{Parse Server Ruby Client. Perform Object-relational mapping between Parse Server and Ruby classes, with authentication, cloud code webhooks, push notifications and more built in. parse-stack-next is a fork of parse-stack with additional features: vector search, Atlas Search, agent ACL scopes, GraphQL, MongoDB-direct pipeline enforcement, and ongoing maintenance.}
12
+ spec.summary = %q{Parse Server SDK for Ruby ORM, queries, auth, and MongoDB-direct access}
13
+ spec.description = %q{Production-grade Ruby client for Parse Server. Map Parse classes to Ruby models with typed properties, associations, and validations; build queries with a chainable DataMapper-style DSL; handle auth, sessions, MFA, cloud code webhooks, and push; introspect and migrate schemas; manage role hierarchies; scope access with per-agent ACL/CLP; generate GraphQL types; run Atlas Search and vector search; and bypass REST for high-throughput reads via the MongoDB-direct pipeline.}
14
14
  spec.homepage = "https://github.com/neurosynq/parse-stack-next"
15
15
  spec.license = "MIT"
16
16
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parse-stack-next
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.1
4
+ version: 5.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anthony Persaud
@@ -195,11 +195,12 @@ dependencies:
195
195
  - - "~>"
196
196
  - !ruby/object:Gem::Version
197
197
  version: '2.0'
198
- description: 'Parse Server Ruby Client. Perform Object-relational mapping between
199
- Parse Server and Ruby classes, with authentication, cloud code webhooks, push notifications
200
- and more built in. parse-stack-next is a fork of parse-stack with additional features:
201
- vector search, Atlas Search, agent ACL scopes, GraphQL, MongoDB-direct pipeline
202
- enforcement, and ongoing maintenance.'
198
+ description: Production-grade Ruby client for Parse Server. Map Parse classes to Ruby
199
+ models with typed properties, associations, and validations; build queries with
200
+ a chainable DataMapper-style DSL; handle auth, sessions, MFA, cloud code webhooks,
201
+ and push; introspect and migrate schemas; manage role hierarchies; scope access
202
+ with per-agent ACL/CLP; generate GraphQL types; run Atlas Search and vector search;
203
+ and bypass REST for high-throughput reads via the MongoDB-direct pipeline.
203
204
  email:
204
205
  - adrian+parse-stack@neurosynq.net
205
206
  executables:
@@ -210,6 +211,9 @@ files:
210
211
  - ".bundle/config"
211
212
  - ".env.sample"
212
213
  - ".env.test"
214
+ - ".github/ISSUE_TEMPLATE/bug_report.yml"
215
+ - ".github/ISSUE_TEMPLATE/feature_request.yml"
216
+ - ".github/dependabot.yml"
213
217
  - ".github/workflows/codeql.yml"
214
218
  - ".github/workflows/docs.yml"
215
219
  - ".github/workflows/release.yml"
@@ -218,6 +222,7 @@ files:
218
222
  - ".ruby-version"
219
223
  - ".solargraph.yml"
220
224
  - ".vscode/settings.json"
225
+ - ".yardopts"
221
226
  - CHANGELOG.md
222
227
  - Gemfile
223
228
  - Gemfile.lock
@@ -238,12 +243,15 @@ files:
238
243
  - config/parse-config.json
239
244
  - docs/TEST_SERVER.md
240
245
  - docs/_config.yml
246
+ - docs/acl_clp_guide.md
241
247
  - docs/atlas_vector_search_guide.md
242
248
  - docs/client_sdk_guide.md
243
249
  - docs/mcp_guide.md
244
250
  - docs/mongodb_direct_guide.md
245
251
  - docs/mongodb_index_optimization_guide.md
246
252
  - docs/usage_guide.md
253
+ - docs/yard-template/default/fulldoc/html/css/common.css
254
+ - docs/yard-template/default/fulldoc/html/css/full_list.css
247
255
  - examples/transaction_example.rb
248
256
  - lib/parse-stack-next.rb
249
257
  - lib/parse-stack.rb
@@ -321,6 +329,8 @@ files:
321
329
  - lib/parse/live_query/health_monitor.rb
322
330
  - lib/parse/live_query/logging.rb
323
331
  - lib/parse/live_query/subscription.rb
332
+ - lib/parse/lock.rb
333
+ - lib/parse/lock_backend.rb
324
334
  - lib/parse/lookup_rewriter.rb
325
335
  - lib/parse/model/acl.rb
326
336
  - lib/parse/model/associations/belongs_to.rb
@@ -445,5 +455,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
445
455
  requirements: []
446
456
  rubygems_version: 3.6.9
447
457
  specification_version: 4
448
- summary: Parse Server Ruby Client SDK (parse-stack-next fork)
458
+ summary: Parse Server SDK for Ruby ORM, queries, auth, and MongoDB-direct access
449
459
  test_files: []