parse-stack-next 4.5.0 → 5.0.1

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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/.bundle/config +2 -0
  3. data/.env.sample +17 -3
  4. data/.github/workflows/codeql.yml +44 -0
  5. data/.github/workflows/docs.yml +39 -0
  6. data/.github/workflows/release.yml +32 -0
  7. data/.github/workflows/ruby.yml +8 -6
  8. data/.gitignore +4 -0
  9. data/.vscode/settings.json +3 -0
  10. data/CHANGELOG.md +305 -72
  11. data/Gemfile.lock +10 -3
  12. data/LICENSE.txt +1 -1
  13. data/README.md +190 -219
  14. data/Rakefile +1 -1
  15. data/SECURITY.md +30 -0
  16. data/assets/parse-stack-next-avatar.png +0 -0
  17. data/assets/parse-stack-next-avatar.svg +37 -0
  18. data/assets/parse-stack-next-banner.png +0 -0
  19. data/assets/parse-stack-next-banner.svg +45 -0
  20. data/assets/parse-stack-next-social-preview.png +0 -0
  21. data/docs/atlas_vector_search_guide.md +511 -0
  22. data/docs/client_sdk_guide.md +1320 -0
  23. data/docs/mcp_guide.md +225 -104
  24. data/docs/mongodb_direct_guide.md +21 -4
  25. data/docs/usage_guide.md +585 -0
  26. data/examples/transaction_example.rb +28 -28
  27. data/lib/parse/acl_scope.rb +2 -2
  28. data/lib/parse/agent/mcp_rack_app.rb +184 -16
  29. data/lib/parse/agent/metadata_dsl.rb +16 -16
  30. data/lib/parse/agent/pipeline_validator.rb +28 -1
  31. data/lib/parse/agent/prompts.rb +5 -5
  32. data/lib/parse/agent/tools.rb +287 -14
  33. data/lib/parse/agent.rb +209 -12
  34. data/lib/parse/api/analytics.rb +27 -5
  35. data/lib/parse/api/files.rb +6 -2
  36. data/lib/parse/api/push.rb +21 -4
  37. data/lib/parse/api/server.rb +59 -0
  38. data/lib/parse/api/users.rb +26 -2
  39. data/lib/parse/atlas_search/index_manager.rb +84 -0
  40. data/lib/parse/atlas_search.rb +37 -9
  41. data/lib/parse/cache/pool.rb +88 -0
  42. data/lib/parse/cache/redis.rb +249 -0
  43. data/lib/parse/client/body_builder.rb +94 -0
  44. data/lib/parse/client/caching.rb +109 -9
  45. data/lib/parse/client/response.rb +27 -0
  46. data/lib/parse/client.rb +74 -3
  47. data/lib/parse/console.rb +203 -0
  48. data/lib/parse/embeddings/cohere.rb +484 -0
  49. data/lib/parse/embeddings/fixture.rb +130 -0
  50. data/lib/parse/embeddings/jina.rb +454 -0
  51. data/lib/parse/embeddings/local_http.rb +492 -0
  52. data/lib/parse/embeddings/openai.rb +520 -0
  53. data/lib/parse/embeddings/provider.rb +264 -0
  54. data/lib/parse/embeddings/qwen.rb +431 -0
  55. data/lib/parse/embeddings/voyage.rb +550 -0
  56. data/lib/parse/embeddings.rb +225 -0
  57. data/lib/parse/graphql/scalars.rb +53 -0
  58. data/lib/parse/graphql/type_generator.rb +264 -0
  59. data/lib/parse/graphql.rb +48 -0
  60. data/lib/parse/live_query/client.rb +24 -5
  61. data/lib/parse/live_query/subscription.rb +17 -6
  62. data/lib/parse/live_query.rb +9 -4
  63. data/lib/parse/model/associations/collection_proxy.rb +2 -2
  64. data/lib/parse/model/associations/has_many.rb +32 -1
  65. data/lib/parse/model/associations/has_one.rb +17 -0
  66. data/lib/parse/model/associations/pointer_collection_proxy.rb +3 -3
  67. data/lib/parse/model/classes/user.rb +307 -11
  68. data/lib/parse/model/clp.rb +1 -1
  69. data/lib/parse/model/core/create_lock.rb +14 -2
  70. data/lib/parse/model/core/embed_managed.rb +296 -0
  71. data/lib/parse/model/core/fetching.rb +4 -4
  72. data/lib/parse/model/core/indexing.rb +53 -14
  73. data/lib/parse/model/core/parse_reference.rb +3 -3
  74. data/lib/parse/model/core/properties.rb +70 -1
  75. data/lib/parse/model/core/querying.rb +57 -1
  76. data/lib/parse/model/core/vector_searchable.rb +285 -0
  77. data/lib/parse/model/file.rb +16 -4
  78. data/lib/parse/model/model.rb +26 -10
  79. data/lib/parse/model/object.rb +63 -6
  80. data/lib/parse/model/pointer.rb +16 -2
  81. data/lib/parse/model/shortnames.rb +2 -0
  82. data/lib/parse/model/validations/uniqueness_validator.rb +3 -3
  83. data/lib/parse/model/vector.rb +102 -0
  84. data/lib/parse/mongodb.rb +90 -8
  85. data/lib/parse/pipeline_security.rb +59 -2
  86. data/lib/parse/query/constraints.rb +16 -14
  87. data/lib/parse/query/ordering.rb +1 -1
  88. data/lib/parse/query.rb +137 -64
  89. data/lib/parse/stack/generators/templates/model.erb +2 -2
  90. data/lib/parse/stack/generators/templates/model_installation.rb +1 -1
  91. data/lib/parse/stack/generators/templates/model_role.rb +1 -1
  92. data/lib/parse/stack/generators/templates/model_session.rb +1 -1
  93. data/lib/parse/stack/generators/templates/parse.rb +1 -1
  94. data/lib/parse/stack/generators/templates/webhooks.rb +1 -1
  95. data/lib/parse/stack/version.rb +1 -1
  96. data/lib/parse/stack.rb +375 -73
  97. data/lib/parse/two_factor_auth/user_extension.rb +5 -2
  98. data/lib/parse/vector_search.rb +341 -0
  99. data/parse-stack-next.gemspec +10 -9
  100. data/scripts/docker/docker-compose.test.yml +18 -0
  101. data/scripts/start-parse.sh +6 -0
  102. data/scripts/vector_prototype/create_vector_index.js +105 -0
  103. data/scripts/vector_prototype/fetch_embeddings.py +241 -0
  104. data/scripts/vector_prototype/fixture_manifest.json +9 -0
  105. data/scripts/vector_prototype/query_prototype.rb +84 -0
  106. data/scripts/vector_prototype/run.sh +34 -0
  107. metadata +77 -5
  108. data/parse-stack.png +0 -0
@@ -20,8 +20,8 @@ module Parse
20
20
  # Third-party apps may register additional tools:
21
21
  #
22
22
  # Parse::Agent::Tools.register(
23
- # name: :breakdown_captures,
24
- # description: "Count captures grouped by user/...",
23
+ # name: :breakdown_posts,
24
+ # description: "Count posts grouped by author/...",
25
25
  # parameters: { type: "object", properties: {...}, required: [...] },
26
26
  # permission: :readonly,
27
27
  # timeout: 30,
@@ -104,6 +104,28 @@ module Parse
104
104
  },
105
105
  required: [],
106
106
  },
107
+ output_schema: {
108
+ type: "object",
109
+ properties: {
110
+ tools: {
111
+ type: "array",
112
+ items: {
113
+ type: "object",
114
+ properties: {
115
+ name: { type: "string" },
116
+ category: { type: "string" },
117
+ description: { type: "string" },
118
+ },
119
+ required: %w[name category description],
120
+ },
121
+ },
122
+ categories: {
123
+ type: "object",
124
+ additionalProperties: { type: "string" },
125
+ },
126
+ },
127
+ required: %w[tools categories],
128
+ },
107
129
  },
108
130
 
109
131
  get_all_schemas: {
@@ -123,6 +145,42 @@ module Parse
123
145
  },
124
146
  required: [],
125
147
  },
148
+ output_schema: {
149
+ type: "object",
150
+ properties: {
151
+ total: { type: "integer", minimum: 0 },
152
+ note: { type: "string" },
153
+ built_in: {
154
+ type: "array",
155
+ items: {
156
+ type: "object",
157
+ properties: {
158
+ name: { type: "string" },
159
+ fields: { type: "integer", minimum: 0 },
160
+ desc: { type: "string" },
161
+ methods: { type: "integer", minimum: 0 },
162
+ },
163
+ required: %w[name fields],
164
+ additionalProperties: true,
165
+ },
166
+ },
167
+ custom: {
168
+ type: "array",
169
+ items: {
170
+ type: "object",
171
+ properties: {
172
+ name: { type: "string" },
173
+ fields: { type: "integer", minimum: 0 },
174
+ desc: { type: "string" },
175
+ methods: { type: "integer", minimum: 0 },
176
+ },
177
+ required: %w[name fields],
178
+ additionalProperties: true,
179
+ },
180
+ },
181
+ },
182
+ required: %w[total built_in custom],
183
+ },
126
184
  },
127
185
 
128
186
  get_schema: {
@@ -144,6 +202,24 @@ module Parse
144
202
  },
145
203
  required: ["class_name"],
146
204
  },
205
+ output_schema: {
206
+ type: "object",
207
+ properties: {
208
+ class_name: { type: "string" },
209
+ type: { type: "string" },
210
+ description: { type: "string" },
211
+ usage: { type: "string" },
212
+ fields: { type: "array", items: { type: "object", additionalProperties: true } },
213
+ indexes: { type: "object", additionalProperties: true },
214
+ permissions: { type: "object", additionalProperties: true },
215
+ agent_methods: { type: "array", items: { type: "object", additionalProperties: true } },
216
+ canonical_filter: { type: "object", additionalProperties: true },
217
+ agent_fields: { type: "array", items: { type: "string" } },
218
+ agent_join_fields: { type: "array", items: { type: "string" } },
219
+ relations: { type: "object", additionalProperties: true },
220
+ },
221
+ required: %w[class_name type fields indexes permissions],
222
+ },
147
223
  },
148
224
 
149
225
  query_class: {
@@ -183,6 +259,50 @@ module Parse
183
259
  },
184
260
  required: ["class_name"],
185
261
  },
262
+ # Polymorphic envelope: the default `format: "json"` returns a row
263
+ # envelope (class_name, result_count, pagination, results, ...);
264
+ # `format: "csv"|"markdown"|"table"` returns a text envelope
265
+ # (class_name, format, headers, row_count, output). Declared here as
266
+ # a permissive superset (every key from either envelope is optional
267
+ # except class_name) because MCP 2025-06-18 expects type:object at
268
+ # the outputSchema root, which precludes a top-level oneOf. Clients
269
+ # that need to disambiguate inspect `format` (absent for the json
270
+ # envelope, present for text envelopes).
271
+ output_schema: {
272
+ type: "object",
273
+ properties: {
274
+ class_name: { type: "string" },
275
+ # json envelope
276
+ result_count: { type: "integer", minimum: 0 },
277
+ pagination: {
278
+ type: "object",
279
+ properties: {
280
+ limit: { type: "integer", minimum: 0 },
281
+ skip: { type: "integer", minimum: 0 },
282
+ has_more: { type: "boolean" },
283
+ },
284
+ required: %w[limit skip has_more],
285
+ },
286
+ truncated: { type: "boolean" },
287
+ truncated_note: { type: "string" },
288
+ truncated_include_fields: { type: "object", additionalProperties: true },
289
+ next_call: {
290
+ type: "object",
291
+ properties: {
292
+ tool: { type: "string" },
293
+ arguments: { type: "object", additionalProperties: true },
294
+ },
295
+ required: %w[tool arguments],
296
+ },
297
+ results: { type: "array", items: { type: "object", additionalProperties: true } },
298
+ # csv / markdown / table envelope
299
+ format: { type: "string", enum: %w[csv markdown table] },
300
+ headers: { type: "array", items: { type: "string" } },
301
+ row_count: { type: "integer", minimum: 0 },
302
+ output: { type: "string" },
303
+ },
304
+ required: %w[class_name],
305
+ },
186
306
  },
187
307
 
188
308
  count_objects: {
@@ -204,6 +324,15 @@ module Parse
204
324
  },
205
325
  required: ["class_name"],
206
326
  },
327
+ output_schema: {
328
+ type: "object",
329
+ properties: {
330
+ class_name: { type: "string" },
331
+ count: { type: "integer", minimum: 0 },
332
+ constraints: { type: "object" },
333
+ },
334
+ required: %w[class_name count constraints],
335
+ },
207
336
  },
208
337
 
209
338
  get_object: {
@@ -228,6 +357,18 @@ module Parse
228
357
  },
229
358
  required: ["class_name", "object_id"],
230
359
  },
360
+ output_schema: {
361
+ type: "object",
362
+ properties: {
363
+ class_name: { type: "string" },
364
+ object_id: { type: "string" },
365
+ created_at: { type: %w[string null] },
366
+ updated_at: { type: %w[string null] },
367
+ object: { type: "object" },
368
+ truncated_include_fields: { type: "object" },
369
+ },
370
+ required: %w[class_name object_id object],
371
+ },
231
372
  },
232
373
 
233
374
  get_objects: {
@@ -253,6 +394,22 @@ module Parse
253
394
  },
254
395
  required: ["class_name", "ids"],
255
396
  },
397
+ output_schema: {
398
+ type: "object",
399
+ properties: {
400
+ class_name: { type: "string" },
401
+ objects: {
402
+ type: "object",
403
+ additionalProperties: { type: "object" },
404
+ description: "Map of objectId => fetched object. Empty when no ids resolved.",
405
+ },
406
+ missing: { type: "array", items: { type: "string" } },
407
+ requested: { type: "integer", minimum: 0 },
408
+ found: { type: "integer", minimum: 0 },
409
+ truncated_include_fields: { type: "object" },
410
+ },
411
+ required: %w[class_name objects missing requested found],
412
+ },
256
413
  },
257
414
 
258
415
  get_sample_objects: {
@@ -270,6 +427,16 @@ module Parse
270
427
  },
271
428
  required: ["class_name"],
272
429
  },
430
+ output_schema: {
431
+ type: "object",
432
+ properties: {
433
+ class_name: { type: "string" },
434
+ sample_count: { type: "integer", minimum: 0 },
435
+ samples: { type: "array", items: { type: "object" } },
436
+ note: { type: "string" },
437
+ },
438
+ required: %w[class_name sample_count samples],
439
+ },
273
440
  },
274
441
 
275
442
  aggregate: {
@@ -383,6 +550,33 @@ module Parse
383
550
  },
384
551
  required: ["class_name", "field"],
385
552
  },
553
+ output_schema: {
554
+ type: "object",
555
+ properties: {
556
+ class_name: { type: "string" },
557
+ field: { type: "string" },
558
+ operation: { type: "string" },
559
+ group_count: { type: "integer", minimum: 0 },
560
+ groups: {
561
+ type: "array",
562
+ items: {
563
+ type: "object",
564
+ properties: {
565
+ key: {},
566
+ value: { type: %w[number null] },
567
+ },
568
+ required: %w[key value],
569
+ },
570
+ },
571
+ value_field: { type: "string" },
572
+ pointer_class: { type: "string" },
573
+ flatten_arrays: { type: "boolean" },
574
+ sort: { type: "string" },
575
+ truncated: { type: "boolean" },
576
+ limit: { type: "integer" },
577
+ },
578
+ required: %w[class_name field operation group_count groups limit],
579
+ },
386
580
  },
387
581
 
388
582
  group_by_date: {
@@ -428,6 +622,33 @@ module Parse
428
622
  },
429
623
  required: ["class_name", "field", "interval"],
430
624
  },
625
+ output_schema: {
626
+ type: "object",
627
+ properties: {
628
+ class_name: { type: "string" },
629
+ field: { type: "string" },
630
+ interval: { type: "string" },
631
+ operation: { type: "string" },
632
+ group_count: { type: "integer", minimum: 0 },
633
+ groups: {
634
+ type: "array",
635
+ items: {
636
+ type: "object",
637
+ properties: {
638
+ key: { type: %w[string null] },
639
+ value: { type: %w[number null] },
640
+ },
641
+ required: %w[key value],
642
+ },
643
+ },
644
+ value_field: { type: "string" },
645
+ timezone: { type: "string" },
646
+ sort: { type: "string" },
647
+ truncated: { type: "boolean" },
648
+ limit: { type: "integer" },
649
+ },
650
+ required: %w[class_name field interval operation group_count groups sort limit],
651
+ },
431
652
  },
432
653
 
433
654
  distinct: {
@@ -459,6 +680,21 @@ module Parse
459
680
  },
460
681
  required: ["class_name", "field"],
461
682
  },
683
+ output_schema: {
684
+ type: "object",
685
+ properties: {
686
+ class_name: { type: "string" },
687
+ field: { type: "string" },
688
+ count: { type: "integer", minimum: 0 },
689
+ values: { type: "array",
690
+ items: { type: %w[string number boolean null] } },
691
+ pointer_class: { type: "string" },
692
+ sort: { type: "string" },
693
+ truncated: { type: "boolean" },
694
+ limit: { type: "integer" },
695
+ },
696
+ required: %w[class_name field count values limit],
697
+ },
462
698
  },
463
699
 
464
700
  export_data: {
@@ -687,6 +923,13 @@ module Parse
687
923
  # @param permission [Symbol] :readonly, :write, or :admin (required)
688
924
  # @param timeout [Integer] seconds before ToolTimeoutError (default: 30)
689
925
  # @param handler [Proc] lambda(agent, **args) -> Hash (required)
926
+ # @param client_safe [Boolean] when +true+, the tool is dispatchable
927
+ # from a client-mode agent (one whose client has no master_key).
928
+ # Default +false+ — registered tools are master-key-only unless
929
+ # the author explicitly declares them safe for session-token
930
+ # contexts. The handler is responsible for routing through
931
+ # +agent.client+ with +agent.session_token+ rather than touching
932
+ # the master key directly.
690
933
  # @raise [ArgumentError] when required kwargs are missing or permission is invalid
691
934
  #
692
935
  # @note SECURITY: Registered tool handlers run as trusted code inside the
@@ -709,7 +952,8 @@ module Parse
709
952
  # register at boot from code you control; never accept registrations
710
953
  # from configuration files at runtime.
711
954
  def register(name:, description:, parameters:, permission:, handler:,
712
- timeout: DEFAULT_TIMEOUT, output_schema: nil, category: "custom")
955
+ timeout: DEFAULT_TIMEOUT, output_schema: nil, category: "custom",
956
+ client_safe: false)
713
957
  unless %i[readonly write admin].include?(permission)
714
958
  raise ArgumentError, "permission must be :readonly, :write, or :admin (got #{permission.inspect})"
715
959
  end
@@ -761,6 +1005,7 @@ module Parse
761
1005
  timeout: timeout.to_i,
762
1006
  handler: handler,
763
1007
  output_schema: output_schema,
1008
+ client_safe: client_safe == true,
764
1009
  }
765
1010
  end
766
1011
  notify_subscribers
@@ -848,18 +1093,19 @@ module Parse
848
1093
  TOOL_TIMEOUTS[sym] || DEFAULT_TIMEOUT
849
1094
  end
850
1095
 
851
- # Resolve the MCP outputSchema for a tool, if any. Only
852
- # registered tools may declare an output_schema in v4.2;
853
- # built-in tools return nil. The dispatcher uses this to decide
854
- # whether to mirror the result data as `structuredContent` in
855
- # the `tools/call` response envelope.
1096
+ # Resolve the MCP outputSchema for a tool, if any. Checks
1097
+ # registered tools first (override path), then falls through to
1098
+ # the built-in TOOL_DEFINITIONS table. The dispatcher uses this
1099
+ # to decide whether to mirror the result data as
1100
+ # `structuredContent` in the `tools/call` response envelope.
856
1101
  #
857
1102
  # @param name [Symbol, String] tool name
858
1103
  # @return [Hash, nil] JSON Schema Hash, or nil if not declared.
859
1104
  def output_schema_for(name)
860
1105
  sym = name.to_sym
861
1106
  entry = REGISTRY_MUTEX.synchronize { @registry[sym] }
862
- entry && entry[:output_schema]
1107
+ return entry[:output_schema] if entry && entry[:output_schema]
1108
+ TOOL_DEFINITIONS.dig(sym, :output_schema)
863
1109
  end
864
1110
 
865
1111
  # Resolve the category for a tool. Registered tools always carry
@@ -876,6 +1122,33 @@ module Parse
876
1122
  TOOL_DEFINITIONS.dig(sym, :category)
877
1123
  end
878
1124
 
1125
+ # Whether a tool is safe to dispatch from a client-mode agent
1126
+ # (one whose client has no master_key). Returns true for:
1127
+ #
1128
+ # * Built-in read tools listed in CLIENT_SAFE_READ_TOOLS — these
1129
+ # route through session-token REST endpoints that Parse Server
1130
+ # natively authorizes (ACL + CLP + protectedFields).
1131
+ # * Built-in mutation tools listed in CLIENT_SAFE_MUTATION_TOOLS —
1132
+ # same REST-native authorization. The caller is responsible for
1133
+ # additionally checking the per-agent +allow_mutations+ gate;
1134
+ # this predicate reports REST-safety only.
1135
+ # * Custom tools registered with +client_safe: true+ — the
1136
+ # registering author has declared the handler safe for
1137
+ # client-mode dispatch.
1138
+ #
1139
+ # Anything else (call_method, aggregate, atlas_*, schema tools,
1140
+ # custom tools registered without the flag) returns false.
1141
+ #
1142
+ # @param name [Symbol, String] tool name
1143
+ # @return [Boolean]
1144
+ def client_safe?(name)
1145
+ sym = name.to_sym
1146
+ return true if Parse::Agent::CLIENT_SAFE_READ_TOOLS.include?(sym)
1147
+ return true if Parse::Agent::CLIENT_SAFE_MUTATION_TOOLS.include?(sym)
1148
+ entry = REGISTRY_MUTEX.synchronize { @registry[sym] }
1149
+ entry ? entry[:client_safe] == true : false
1150
+ end
1151
+
879
1152
  # Returns all tool names: builtins + registered.
880
1153
  #
881
1154
  # @return [Array<Symbol>]
@@ -2048,7 +2321,7 @@ module Parse
2048
2321
  # dotted path of their own — that's the explicit "I named exactly
2049
2322
  # what I want" signal. It's also suppressed when no `keys:` was
2050
2323
  # passed at all (caller chose full-row mode) and when the include is
2051
- # multi-hop (`user.team`); auto-projection is one-hop only.
2324
+ # multi-hop (`author.workspace`); auto-projection is one-hop only.
2052
2325
  #
2053
2326
  # Pointer-to-joined-class resolution uses the parent class's
2054
2327
  # `references` reflection (same source `walk_pointer_path!` uses),
@@ -2087,8 +2360,8 @@ module Parse
2087
2360
  h[root] << k
2088
2361
  end
2089
2362
 
2090
- # Only consider one-hop includes; multi-hop (`user.team`) leaves
2091
- # the user projection alone and lets the deeper hop materialize
2363
+ # Only consider one-hop includes; multi-hop (`author.workspace`) leaves
2364
+ # the author projection alone and lets the deeper hop materialize
2092
2365
  # fully — keeps the auto-expansion bounded and avoids walking the
2093
2366
  # full relation graph at query time.
2094
2367
  single_hop_includes = include_arr.map(&:to_s).reject { |p| p.include?(".") }
@@ -4313,7 +4586,7 @@ module Parse
4313
4586
  lines = []
4314
4587
  lines << "| #{headers.join(" | ")} |"
4315
4588
  lines << "| #{headers.map { "---" }.join(" | ")} |"
4316
- rows.each { |r| lines << "| #{r.map { |c| c.to_s.gsub("|", "\\|") }.join(" | ")} |" }
4589
+ rows.each { |r| lines << "| #{r.map { |c| c.to_s.gsub(/\r?\n/, " ").gsub(/([\\|])/, '\\\\\1') }.join(" | ")} |" }
4317
4590
  lines.join("\n")
4318
4591
  end
4319
4592
  module_function :format_export_markdown
@@ -4840,7 +5113,7 @@ module Parse
4840
5113
  # 2. Must be an Array.
4841
5114
  # 3. Length must not exceed {MAX_INCLUDE_FIELDS}.
4842
5115
  # 4. Each entry must match /\A[A-Za-z][A-Za-z0-9_.]{0,127}\z/
4843
- # (allows dotted pointer paths like "author.team"; rejects any entry
5116
+ # (allows dotted pointer paths like "author.workspace"; rejects any entry
4844
5117
  # beginning with an underscore such as "_session_token").
4845
5118
  #
4846
5119
  # @param include [Array<String>, nil] the include parameter to validate.