calmlens 0.18.3 → 0.20.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/README.md +3 -1
- data/lib/calmlens/assets.rb +142 -42
- data/lib/calmlens/cdn.rb +14 -7
- data/lib/calmlens/error_codes.rb +28 -2
- data/lib/calmlens/events.rb +5 -2
- data/lib/calmlens/folders.rb +9 -6
- data/lib/calmlens/hash_lists.rb +11 -8
- data/lib/calmlens/internal.rb +10 -0
- data/lib/calmlens/lenses.rb +6 -2
- data/lib/calmlens/option_keys.rb +89 -0
- data/lib/calmlens/policies.rb +100 -0
- data/lib/calmlens/resource.rb +32 -1
- data/lib/calmlens/server_client.rb +23 -6
- data/lib/calmlens/session.rb +6 -3
- data/lib/calmlens/testing/classification.rb +3 -0
- data/lib/calmlens/testing/fake_api.rb +206 -20
- data/lib/calmlens/testing/test_image_grid.rb +161 -0
- data/lib/calmlens/testing/test_input_scenario.rb +127 -0
- data/lib/calmlens/testing/test_inputs.rb +157 -0
- data/lib/calmlens/testing.rb +10 -2
- data/lib/calmlens/text.rb +5 -4
- data/lib/calmlens/transform_rules.rb +120 -0
- data/lib/calmlens/transform_schema.rb +236 -0
- data/lib/calmlens/transforms.rb +37 -28
- data/lib/calmlens/upload_body.rb +14 -0
- data/lib/calmlens/upload_mime_types.rb +43 -0
- data/lib/calmlens/upload_tokens.rb +4 -1
- data/lib/calmlens/version.rb +1 -1
- data/lib/calmlens.rb +5 -0
- data/sig/calmlens.rbs +59 -3
- data/sig/error_codes.rbs +14 -1
- data/sig/types.rbs +104 -20
- metadata +10 -2
data/lib/calmlens/transforms.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module CalmLens
|
|
4
|
-
# CDN transform selectors: selector Strings and structured
|
|
4
|
+
# CDN transform selectors: selector Strings and structured options serialized the way every SDK does.
|
|
5
5
|
# @api private
|
|
6
6
|
module Transforms
|
|
7
7
|
module_function
|
|
@@ -14,46 +14,55 @@ module CalmLens
|
|
|
14
14
|
# </generated: image-transform-colours>
|
|
15
15
|
HEX_COLOUR = /\A#((?:\h{3}){1,2}|(?:\h{4}){1,2})\z/
|
|
16
16
|
|
|
17
|
-
# Returns the selector the API and CDN accept for a transform: a selector String
|
|
18
|
-
# structured
|
|
17
|
+
# Returns the selector the API and CDN accept for a transform: a selector String checked and
|
|
18
|
+
# unchanged, or structured options (`{width: 640, fit: 'cover'}`) serialized by {serialize}.
|
|
19
19
|
def selector(value)
|
|
20
|
-
value.nil? || value.is_a?(String)
|
|
20
|
+
return serialize(value) unless value.nil? || value.is_a?(String)
|
|
21
|
+
# A transform is one path segment of options: it must not add a segment, lens, query or fragment.
|
|
22
|
+
if value&.match?(%r{[/?#]}) || value&.include?('..') || value&.start_with?('@')
|
|
23
|
+
raise Internal.invalid('Transform cannot contain "/", "..", "?" or "#", or start with "@"')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
value
|
|
21
27
|
end
|
|
22
28
|
|
|
23
|
-
# Builds the CDN path of a transform. A selector String
|
|
24
|
-
#
|
|
29
|
+
# Builds the CDN path text of a transform. A selector String is encoded like JavaScript's
|
|
30
|
+
# encodeURI; serialized options are already path-safe.
|
|
25
31
|
def path(value)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
text = value.sub(%r{\A/+}, '')
|
|
29
|
-
raise Internal.invalid('Transform cannot contain "..", "?" or "#"') if text.include?('..') || text.match?(/[?#]/)
|
|
32
|
+
text = selector(value)
|
|
33
|
+
return text unless value.is_a?(String)
|
|
30
34
|
|
|
31
35
|
# Like JavaScript's encodeURI.
|
|
32
36
|
text.b.gsub(%r{[^A-Za-z0-9\-_.!~*'();/:@&=+$,]}n) { |byte| format('%%%02X', byte.ord) }
|
|
33
37
|
end
|
|
34
38
|
|
|
35
|
-
# Serializes structured
|
|
36
|
-
#
|
|
37
|
-
#
|
|
39
|
+
# Serializes structured transform options as a CDN selector, such as `fit=cover,width=640`. Option
|
|
40
|
+
# keys are sorted so equal transforms share a URL, nested keys are dotted (`border.color`), hex
|
|
41
|
+
# colours are written without `#` and values are percent-encoded.
|
|
38
42
|
def serialize(value)
|
|
39
|
-
|
|
40
|
-
raise Internal.invalid('Transform needs at least one step') if steps.empty?
|
|
43
|
+
raise Internal.invalid('Transform options must be a Hash') unless value.is_a?(Hash)
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
assert_valid(value)
|
|
46
|
+
pairs = []
|
|
47
|
+
sorted_options(value).each { |key, item| append_pairs(pairs, key, item) }
|
|
48
|
+
raise Internal.invalid('A transform needs at least one option') if pairs.empty?
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
options = step['options'] || {}
|
|
49
|
-
raise Internal.invalid('Transform options must be a Hash') unless options.is_a?(Hash)
|
|
50
|
-
|
|
51
|
-
pairs = []
|
|
52
|
-
sorted_options(options).each { |key, item| append_pairs(pairs, key, item) }
|
|
53
|
-
raise Internal.invalid('Transform steps need at least one option') if pairs.empty?
|
|
50
|
+
pairs.join(',')
|
|
51
|
+
end
|
|
54
52
|
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
# Raises `sdk/invalid-argument` naming each transform option the CDN would refuse, with
|
|
54
|
+
# `details` `{'unknownKeys', 'invalidKeys'}`.
|
|
55
|
+
def assert_valid(options)
|
|
56
|
+
unknown, invalid = TransformRules.check(options)
|
|
57
|
+
return if unknown.empty? && invalid.empty?
|
|
58
|
+
|
|
59
|
+
problems = []
|
|
60
|
+
problems << "unknown option#{'s' if unknown.size > 1} #{unknown.join(', ')}" unless unknown.empty?
|
|
61
|
+
problems << "invalid value#{'s' if invalid.size > 1} for #{invalid.join(', ')}" unless invalid.empty?
|
|
62
|
+
raise InvalidArgumentError.new(SdkErrorCodes::INVALID_ARGUMENT,
|
|
63
|
+
"Transform options are invalid: #{problems.join('; ')}. Pass the options " \
|
|
64
|
+
"themselves, such as { width: 640, format: 'webp' }.",
|
|
65
|
+
details: { 'unknownKeys' => unknown, 'invalidKeys' => invalid })
|
|
57
66
|
end
|
|
58
67
|
|
|
59
68
|
def sorted_options(options, prefix = '')
|
data/lib/calmlens/upload_body.rb
CHANGED
|
@@ -90,6 +90,14 @@ module CalmLens
|
|
|
90
90
|
@source.rewind unless @source.is_a?(String)
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
+
# `length` bytes from `offset` as a body of their own, read again from the source for each
|
|
94
|
+
# attempt. Only for a {#replayable?} body of known size: an IO is positioned at the slice.
|
|
95
|
+
def slice(offset, length)
|
|
96
|
+
return UploadBody.new(@source.byteslice(offset, length)) if @source.is_a?(String)
|
|
97
|
+
|
|
98
|
+
UploadBody.new(@source.slice(offset, length))
|
|
99
|
+
end
|
|
100
|
+
|
|
93
101
|
# The bytes, read fully. Used only by the offline mock.
|
|
94
102
|
def read_all
|
|
95
103
|
return @source if @source.is_a?(String)
|
|
@@ -114,6 +122,12 @@ module CalmLens
|
|
|
114
122
|
|
|
115
123
|
def rewindable? = !@start.nil?
|
|
116
124
|
|
|
125
|
+
# A reader of `length` bytes from `offset` of this rewindable stream, positioned there.
|
|
126
|
+
def slice(offset, length)
|
|
127
|
+
@io.seek(@start + offset)
|
|
128
|
+
IOReader.new(@io, length)
|
|
129
|
+
end
|
|
130
|
+
|
|
117
131
|
def rewind
|
|
118
132
|
@io.seek(@start)
|
|
119
133
|
@remaining = @size
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Generated by packages/sdk-contract/generate.ts from packages/shared/src/UploadMimeTypes.ts; do not edit.
|
|
4
|
+
# After lower-casing and dropping parameters, a type in REJECTED is refused; otherwise one
|
|
5
|
+
# that starts with a PREFIXES entry, ends with a SUFFIXES entry or is in TYPES is accepted,
|
|
6
|
+
# and any other type is refused with upload/invalid-mime-type.
|
|
7
|
+
|
|
8
|
+
module CalmLens
|
|
9
|
+
module Internal
|
|
10
|
+
UPLOAD_MIME_REJECTED = [
|
|
11
|
+
'application/octet-stream',
|
|
12
|
+
'multipart/form-data',
|
|
13
|
+
'multipart/mixed',
|
|
14
|
+
'multipart/alternative',
|
|
15
|
+
'multipart/related',
|
|
16
|
+
'multipart/signed',
|
|
17
|
+
'multipart/encrypted'
|
|
18
|
+
].freeze
|
|
19
|
+
UPLOAD_MIME_PREFIXES = [
|
|
20
|
+
'image/',
|
|
21
|
+
'video/',
|
|
22
|
+
'audio/',
|
|
23
|
+
'text/'
|
|
24
|
+
].freeze
|
|
25
|
+
UPLOAD_MIME_SUFFIXES = [
|
|
26
|
+
'+json',
|
|
27
|
+
'+xml'
|
|
28
|
+
].freeze
|
|
29
|
+
UPLOAD_MIME_TYPES = [
|
|
30
|
+
'application/pdf',
|
|
31
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
32
|
+
'application/msword',
|
|
33
|
+
'application/rtf',
|
|
34
|
+
'application/vnd.oasis.opendocument.text',
|
|
35
|
+
'application/javascript',
|
|
36
|
+
'application/json',
|
|
37
|
+
'application/x-javascript',
|
|
38
|
+
'application/x-yaml',
|
|
39
|
+
'application/xml',
|
|
40
|
+
'application/yaml'
|
|
41
|
+
].freeze
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -9,6 +9,9 @@ module CalmLens
|
|
|
9
9
|
# Signs upload tokens, which let a browser upload through an upload preset. Reach it through
|
|
10
10
|
# {ServerClient#upload_tokens}. Signing happens locally with the API key; no request is made.
|
|
11
11
|
class UploadTokens < Resource
|
|
12
|
+
# The prefix of this resource's operations in Internal::OPTION_KEYS.
|
|
13
|
+
OPERATIONS = 'uploadTokens'
|
|
14
|
+
|
|
12
15
|
TOKEN_PREFIX = 'clut_'
|
|
13
16
|
SIGNING_CONTEXT = 'calmlens-upload-token:v1:'
|
|
14
17
|
DEFAULT_TTL_SECONDS = 600
|
|
@@ -26,7 +29,7 @@ module CalmLens
|
|
|
26
29
|
# @option options [Integer] :expires_in_seconds lifetime, 1 to 3600 (default 600)
|
|
27
30
|
# @return [Hash] `{'token' => String, 'expiresAt' => Integer}` (milliseconds since the Unix epoch)
|
|
28
31
|
def sign(options)
|
|
29
|
-
call(options) { UploadTokens.sign_at(@api_key, options, now: (Time.now.to_f * 1000).to_i, nonce: SecureRandom.uuid) }
|
|
32
|
+
call(options, __method__) { UploadTokens.sign_at(@api_key, options, now: (Time.now.to_f * 1000).to_i, nonce: SecureRandom.uuid) }
|
|
30
33
|
end
|
|
31
34
|
|
|
32
35
|
# Signs like {#sign} at a fixed time with a fixed nonce. {MockServerClient.sign_upload_token}
|
data/lib/calmlens/version.rb
CHANGED
data/lib/calmlens.rb
CHANGED
|
@@ -8,8 +8,12 @@ end
|
|
|
8
8
|
require_relative 'calmlens/version'
|
|
9
9
|
require_relative 'calmlens/error_codes'
|
|
10
10
|
require_relative 'calmlens/errors'
|
|
11
|
+
require_relative 'calmlens/option_keys'
|
|
12
|
+
require_relative 'calmlens/transform_schema'
|
|
13
|
+
require_relative 'calmlens/upload_mime_types'
|
|
11
14
|
require_relative 'calmlens/internal'
|
|
12
15
|
require_relative 'calmlens/transforms'
|
|
16
|
+
require_relative 'calmlens/transform_rules'
|
|
13
17
|
require_relative 'calmlens/response'
|
|
14
18
|
require_relative 'calmlens/transport'
|
|
15
19
|
require_relative 'calmlens/upload_body'
|
|
@@ -19,6 +23,7 @@ require_relative 'calmlens/moderation_context'
|
|
|
19
23
|
require_relative 'calmlens/assets'
|
|
20
24
|
require_relative 'calmlens/folders'
|
|
21
25
|
require_relative 'calmlens/hash_lists'
|
|
26
|
+
require_relative 'calmlens/policies'
|
|
22
27
|
require_relative 'calmlens/text'
|
|
23
28
|
require_relative 'calmlens/upload_tokens'
|
|
24
29
|
require_relative 'calmlens/events'
|
data/sig/calmlens.rbs
CHANGED
|
@@ -109,10 +109,14 @@ module CalmLens
|
|
|
109
109
|
RETRY_MAX_DELAY: Integer
|
|
110
110
|
RETRY_AFTER_MAX: Integer
|
|
111
111
|
DEFAULT_MAX_RESPONSE_BYTES: Integer
|
|
112
|
+
DEFAULT_PART_SIZE_BYTES: Integer
|
|
113
|
+
MIN_PART_SIZE_BYTES: Integer
|
|
114
|
+
MAX_PART_SIZE_BYTES: Integer
|
|
112
115
|
ENSURE_LENSES_MAX_PER_REQUEST: Integer
|
|
113
116
|
attr_reader assets: Assets
|
|
114
117
|
attr_reader folders: Folders
|
|
115
118
|
attr_reader hash_lists: HashLists
|
|
119
|
+
attr_reader policies: Policies
|
|
116
120
|
attr_reader text: Text
|
|
117
121
|
attr_reader upload_tokens: UploadTokens
|
|
118
122
|
attr_reader events: Events
|
|
@@ -122,7 +126,7 @@ module CalmLens
|
|
|
122
126
|
attr_reader cdn_base_url: String
|
|
123
127
|
def initialize: (api_key: String, project_id: String, ?base_url: String, ?cdn_base_url: String, ?timeout: number,
|
|
124
128
|
?open_timeout: number, ?upload_timeout: number?, ?max_retries: Integer, ?retry_base_delay: number,
|
|
125
|
-
?max_response_bytes: Integer, ?transport: Transport::_Transport?) -> void
|
|
129
|
+
?max_response_bytes: Integer, ?part_size_bytes: Integer, ?transport: Transport::_Transport?) -> void
|
|
126
130
|
def close: () -> nil
|
|
127
131
|
def ensure_lenses: (ensure_lenses_options options) -> Array[lens]
|
|
128
132
|
def ensure_lens: (ensure_lens_options options) -> lens
|
|
@@ -200,6 +204,17 @@ module CalmLens
|
|
|
200
204
|
def remove_entry: (hash_list_entry_id_options options) -> nil
|
|
201
205
|
end
|
|
202
206
|
|
|
207
|
+
class Policies < Resource
|
|
208
|
+
def list: (?list_policies_options options) -> policy_list
|
|
209
|
+
def retrieve: (policy_id_options options) -> policy
|
|
210
|
+
def create: (create_policy_options options) -> policy
|
|
211
|
+
def update: (update_policy_options options) -> policy
|
|
212
|
+
def apply_preset: (apply_policy_preset_options options) -> policy
|
|
213
|
+
def set_automatic_updates: (set_policy_automatic_updates_options options) -> policy
|
|
214
|
+
def delete: (policy_id_options options) -> nil
|
|
215
|
+
def assign: (assign_policy_options options) -> policy
|
|
216
|
+
end
|
|
217
|
+
|
|
203
218
|
class UploadTokens < Resource
|
|
204
219
|
TOKEN_PREFIX: String
|
|
205
220
|
SIGNING_CONTEXT: String
|
|
@@ -338,11 +353,12 @@ module CalmLens
|
|
|
338
353
|
def initialize: (?project_id: String, ?api_key: String, ?base_url: String, ?cdn_base_url: String, ?now: _Clock, ?create_id: _IdFactory,
|
|
339
354
|
?moderate: _Moderate?, ?timeout: number,
|
|
340
355
|
?open_timeout: number, ?upload_timeout: number?, ?max_retries: Integer, ?retry_base_delay: number,
|
|
341
|
-
?max_response_bytes: Integer) -> void
|
|
356
|
+
?max_response_bytes: Integer, ?part_size_bytes: Integer) -> void
|
|
342
357
|
# Replaces the mock API key's grants for later requests; the key stays the same.
|
|
343
358
|
def api_key_grants=: (Array[Hash[Symbol | String, untyped]] grants) -> Array[Hash[Symbol | String, untyped]]
|
|
344
359
|
# Fails the next request (or the next one `operation` sends) with `transport-error`, `timeout`, an
|
|
345
|
-
# API error code (with optional `details` and `Retry-After` seconds
|
|
360
|
+
# API error code (with optional `details` and `Retry-After` seconds, which only an API error takes) or
|
|
361
|
+
# an exception.
|
|
346
362
|
def fail_next_request: (?(String | Exception) failure, ?operation: String?, ?details: Hash[String | Symbol, untyped]?,
|
|
347
363
|
?retry_after: Integer?) -> self
|
|
348
364
|
def state: () -> Testing::State
|
|
@@ -398,5 +414,45 @@ module CalmLens
|
|
|
398
414
|
module MockClassification
|
|
399
415
|
CATEGORIES: Array[String]
|
|
400
416
|
QUALIFIERS: Hash[String, Array[String]]
|
|
417
|
+
# Qualifiers the input type sets, which a TestInputs scenario cannot list.
|
|
418
|
+
INPUT_QUALIFIERS: Array[String]
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
# Signed test inputs: text codes, test images and media phrases signed with a project's secret
|
|
422
|
+
# test input key from the portal.
|
|
423
|
+
module TestInputs
|
|
424
|
+
type action = "allow" | "limit" | "age-restrict" | "block"
|
|
425
|
+
type region = "DE" | "AT" | "FR" | "GB" | "EU" | "AU" | "BR"
|
|
426
|
+
type regional_action = { "region" => region, "action" => "limit" | "age-restrict" | "block" }
|
|
427
|
+
# One qualifying instance; set an action on every instance or on none.
|
|
428
|
+
type test_instance = { "category" => classification_category, ?"score" => number, ?"action" => action, "qualifiers" => Array[String] }
|
|
429
|
+
# A scenario with String keys; Symbol and snake_case keys are accepted as a plain Hash.
|
|
430
|
+
type scenario = { ?"regionalActions" => Array[regional_action], ?"unassessedCategories" => Array[classification_category],
|
|
431
|
+
"instances" => Array[test_instance] }
|
|
432
|
+
type signal = classification_category | "sexual.involving-minors" | "harassment.insult" | "sexual.adult"
|
|
433
|
+
| "hate.regional-symbol" | "violence.graphic"
|
|
434
|
+
type media_signal = classification_category | "violence.graphic"
|
|
435
|
+
type input = scenario | Hash[String | Symbol, untyped] | signal
|
|
436
|
+
|
|
437
|
+
# A painted test image: width by height pixels of 8-bit RGBA in the binary String `rgba`, and
|
|
438
|
+
# the same image as binary PNG bytes in `png`.
|
|
439
|
+
class Image < Struct[untyped]
|
|
440
|
+
attr_accessor width: Integer
|
|
441
|
+
attr_accessor height: Integer
|
|
442
|
+
attr_accessor rgba: String
|
|
443
|
+
attr_accessor png: String
|
|
444
|
+
def initialize: (width: Integer, height: Integer, rgba: String, png: String) -> void
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
REGIONS: Array[region]
|
|
448
|
+
SIGNALS: Array[signal]
|
|
449
|
+
MEDIA_SIGNALS: Array[media_signal]
|
|
450
|
+
# clean, limited, adult_image, adult_text, regional_delivery, germany and mixed (deeply frozen).
|
|
451
|
+
PRESETS: Hash[Symbol, scenario]
|
|
452
|
+
|
|
453
|
+
def self.for_signal: (signal signal, ?("text" | "image") kind) -> scenario
|
|
454
|
+
def self.generate_text_code: (input input, String test_input_key) -> String
|
|
455
|
+
def self.paint_image: (input input, String test_input_key) -> Image
|
|
456
|
+
def self.generate_media_code: (media_signal signal, String test_input_key) -> String
|
|
401
457
|
end
|
|
402
458
|
end
|
data/sig/error_codes.rbs
CHANGED
|
@@ -7,6 +7,8 @@ module CalmLens
|
|
|
7
7
|
INVALID_VIEWER_REGION: String
|
|
8
8
|
VIEWER_REGION_REQUIRED: String
|
|
9
9
|
REGIONAL_EVIDENCE_REQUIRED: String
|
|
10
|
+
SIGNED_URL_EXPIRED: String
|
|
11
|
+
SIGNED_URL_INVALID: String
|
|
10
12
|
INVALID_TYPE: String
|
|
11
13
|
TOO_SMALL: String
|
|
12
14
|
TOO_BIG: String
|
|
@@ -34,9 +36,11 @@ module CalmLens
|
|
|
34
36
|
API_KEY_NOT_ACCEPTED: String
|
|
35
37
|
PERMISSION_DENIED: String
|
|
36
38
|
FILE_TOO_LARGE: String
|
|
37
|
-
MULTIPART_IMAGE_TOO_SMALL: String
|
|
38
39
|
MULTIPART_IMAGE_TOO_LARGE: String
|
|
39
40
|
IMAGE_EXCEEDED_MAX_DIMENSIONS: String
|
|
41
|
+
IMAGE_INVALID_INPUT: String
|
|
42
|
+
IMAGE_UNSUPPORTED_FORMAT: String
|
|
43
|
+
IMAGE_INPUT_LIMIT: String
|
|
40
44
|
UPLOAD_LIMIT_EXCEEDED: String
|
|
41
45
|
SPEND_LIMIT_REACHED: String
|
|
42
46
|
INVALID_UPLOAD_MIME_TYPE: String
|
|
@@ -77,6 +81,15 @@ module CalmLens
|
|
|
77
81
|
HASH_LIST_LIMIT_REACHED: String
|
|
78
82
|
HASH_LIST_ENTRY_LIMIT_REACHED: String
|
|
79
83
|
HASH_LIST_ASSET_NOT_HASHED: String
|
|
84
|
+
POLICY_NOT_FOUND: String
|
|
85
|
+
POLICY_REVISION_CONFLICT: String
|
|
86
|
+
POLICY_PROJECTS_CHANGED: String
|
|
87
|
+
POLICY_IN_USE: String
|
|
88
|
+
POLICY_USED_BY_ANOTHER_PROJECT: String
|
|
89
|
+
POLICY_NO_PRESET: String
|
|
90
|
+
POLICY_PRESET_VERSION_CONFLICT: String
|
|
91
|
+
POLICY_PRESET_UPDATE_UNAVAILABLE: String
|
|
92
|
+
POLICY_AUTOMATIC_UPDATES_UNAVAILABLE: String
|
|
80
93
|
UPLOAD_PRESET_NOT_FOUND: String
|
|
81
94
|
UPLOAD_PRESET_DISABLED: String
|
|
82
95
|
UPLOAD_PRESET_TOKEN_REQUIRED: String
|
data/sig/types.rbs
CHANGED
|
@@ -60,8 +60,8 @@ module CalmLens
|
|
|
60
60
|
# Earlier posts to send, oldest first; only the posts CalmLens uses are sent.
|
|
61
61
|
type moderation_context_input = { items: Array[moderation_context_item_input] }
|
|
62
62
|
|
|
63
|
-
# ProcessingFeatures.ts: optional analyses to run, keyed by feature.
|
|
64
|
-
# `enabled` is true.
|
|
63
|
+
# ProcessingFeatures.ts: optional analyses to run, keyed by feature. Link detection runs unless
|
|
64
|
+
# `enabled` is false; every other feature is off unless `enabled` is true.
|
|
65
65
|
type processing_features_input = {
|
|
66
66
|
?ocr: { enabled: bool }?,
|
|
67
67
|
?person_detection: { enabled: bool }?,
|
|
@@ -308,21 +308,25 @@ module CalmLens
|
|
|
308
308
|
# Link safety (preview, LinkSafety.ts).
|
|
309
309
|
# What a link's destination is: `safe` when every reputation check passed, `adult` or `gambling`
|
|
310
310
|
# from its domain's content categories, `phishing` or `malware` from a threat list or the domain's
|
|
311
|
-
# security risks, and `unresolved` when a short link could not be followed or a
|
|
311
|
+
# security risks, and `unresolved` when a short link or redirect chain could not be followed or a
|
|
312
|
+
# check failed. Every address on a link's way is checked, and the most severe verdict is the
|
|
313
|
+
# link's.
|
|
312
314
|
type link_safety_verdict = "safe" | "adult" | "gambling" | "phishing" | "malware" | "unresolved"
|
|
313
315
|
# The verdicts a policy can act on.
|
|
314
316
|
type flagged_link_verdict = "phishing" | "malware" | "adult" | "gambling" | "unresolved"
|
|
315
317
|
# What a link does to its asset, weakest first. Purging content over a link is never offered.
|
|
316
318
|
type link_safety_action = "allow" | "limit" | "age-restrict" | "block"
|
|
317
|
-
# One reputation check of a link
|
|
318
|
-
# of
|
|
319
|
+
# One reputation check of a link, every address it redirects through and its destination: their
|
|
320
|
+
# domain categories, or a threat-list lookup of their URLs. `failed` makes the link `unresolved`;
|
|
321
|
+
# `labels` is what the check reported for any of the addresses.
|
|
319
322
|
type link_safety_evidence = {
|
|
320
323
|
"check" => "domain-categories" | "threat-lists",
|
|
321
324
|
"status" => "checked" | "failed",
|
|
322
325
|
"labels" => Array[String]
|
|
323
326
|
}
|
|
324
327
|
# A link in the asset and what its destination is. `finalUrl` is where it leads after `redirects`
|
|
325
|
-
# redirects, or `null` when a short link could not be
|
|
328
|
+
# redirects, or `null` when a short link or redirect chain could not be
|
|
329
|
+
# followed.
|
|
326
330
|
type checked_link = {
|
|
327
331
|
"url" => String,
|
|
328
332
|
"finalUrl" => String?,
|
|
@@ -450,7 +454,8 @@ module CalmLens
|
|
|
450
454
|
?blur: number,
|
|
451
455
|
?redact: {
|
|
452
456
|
?text: bool,
|
|
453
|
-
?personalInformation: bool
|
|
457
|
+
?personalInformation: bool,
|
|
458
|
+
?faces: bool
|
|
454
459
|
},
|
|
455
460
|
?border: {
|
|
456
461
|
?color: String,
|
|
@@ -496,7 +501,8 @@ module CalmLens
|
|
|
496
501
|
?"blur" => number,
|
|
497
502
|
?"redact" => {
|
|
498
503
|
?"text" => bool,
|
|
499
|
-
?"personalInformation" => bool
|
|
504
|
+
?"personalInformation" => bool,
|
|
505
|
+
?"faces" => bool
|
|
500
506
|
},
|
|
501
507
|
?"border" => {
|
|
502
508
|
?"color" => String,
|
|
@@ -533,12 +539,9 @@ module CalmLens
|
|
|
533
539
|
}
|
|
534
540
|
# </generated: image-transform-options>
|
|
535
541
|
|
|
536
|
-
#
|
|
537
|
-
|
|
538
|
-
type
|
|
539
|
-
# A selector String with one step per path segment, such as `fit=cover,width=1024/blur=12`, or
|
|
540
|
-
# structured steps, which the SDK serializes into a selector.
|
|
541
|
-
type transform_input = String | transform_step_input | Array[transform_step_input]
|
|
542
|
+
# Transform options applied in one pass: a selector String such as `width=1024,fit=cover`, or
|
|
543
|
+
# structured options, which the SDK serializes into a selector.
|
|
544
|
+
type transform_input = String | image_transform_options_input
|
|
542
545
|
|
|
543
546
|
type lens_type = "image" | "video" | "audio" | "document" | "website"
|
|
544
547
|
# ENSURE_LENS_BODY_SCHEMA. Changing a saved lens needs a greater `version` (default 1).
|
|
@@ -549,7 +552,7 @@ module CalmLens
|
|
|
549
552
|
active: bool,
|
|
550
553
|
?description: String?,
|
|
551
554
|
?version: Integer,
|
|
552
|
-
?
|
|
555
|
+
?transform: image_transform_options_input?
|
|
553
556
|
}
|
|
554
557
|
type lens = {
|
|
555
558
|
"id" => String,
|
|
@@ -560,7 +563,7 @@ module CalmLens
|
|
|
560
563
|
?"description" => String?,
|
|
561
564
|
"type" => lens_type,
|
|
562
565
|
"active" => bool,
|
|
563
|
-
?"
|
|
566
|
+
?"transform" => image_transform_options?,
|
|
564
567
|
"createdAt" => Integer,
|
|
565
568
|
?"updatedAt" => Integer
|
|
566
569
|
}
|
|
@@ -619,6 +622,49 @@ module CalmLens
|
|
|
619
622
|
"ordering" => Array[order]
|
|
620
623
|
}
|
|
621
624
|
|
|
625
|
+
# ---- Policies (Policy.ts) ---------------------------------------------------------------------
|
|
626
|
+
|
|
627
|
+
type policy_preset_id = "general-community" | "children-platform" | "education-support" | "wholesome-community"
|
|
628
|
+
# What a policy does with one category: `action` when its policy score reaches 0.5 (default
|
|
629
|
+
# `block`), allowances that let covered content through or act on it more softly, and
|
|
630
|
+
# restrictions, which outweigh allowances.
|
|
631
|
+
type category_policy_settings = {
|
|
632
|
+
?"action" => flagged_content_action,
|
|
633
|
+
?"allowances" => Array[policy_rule_snapshot],
|
|
634
|
+
?"restrictions" => Array[policy_rule_snapshot]
|
|
635
|
+
}
|
|
636
|
+
# Settings by category (`sexual`, `violence`, ...), and optionally link safety.
|
|
637
|
+
type policy_settings = {
|
|
638
|
+
"categorySettings" => Hash[String, category_policy_settings],
|
|
639
|
+
?"linkSafety" => link_safety_settings
|
|
640
|
+
}
|
|
641
|
+
# Where a policy started from a built-in preset. `version` is the release it is pinned to,
|
|
642
|
+
# `latestVersion` the newest; `own` is whether this is the organisation's own policy for the preset
|
|
643
|
+
# rather than a copy made from it. An organisation has at most one per preset, and only it can
|
|
644
|
+
# follow automatic updates.
|
|
645
|
+
type policy_preset_origin = {
|
|
646
|
+
"id" => policy_preset_id,
|
|
647
|
+
"version" => Integer,
|
|
648
|
+
"latestVersion" => Integer,
|
|
649
|
+
"own" => bool
|
|
650
|
+
}
|
|
651
|
+
# A moderation policy. Policies belong to the project's organisation and its projects share them.
|
|
652
|
+
# Send `revision` with each change; a policy changed since is refused with `policy/revision-conflict`.
|
|
653
|
+
type policy = {
|
|
654
|
+
"id" => String,
|
|
655
|
+
"name" => String,
|
|
656
|
+
"description" => String,
|
|
657
|
+
"settings" => policy_settings,
|
|
658
|
+
"revision" => Integer,
|
|
659
|
+
"automaticUpdates" => bool,
|
|
660
|
+
"preset" => policy_preset_origin?,
|
|
661
|
+
"projectIds" => Array[String],
|
|
662
|
+
"createdAt" => Integer,
|
|
663
|
+
"updatedAt" => Integer
|
|
664
|
+
}
|
|
665
|
+
# Every policy of the project's organisation, oldest first, and the one the project moderates with.
|
|
666
|
+
type policy_list = { "policies" => Array[policy], "projectPolicyId" => String }
|
|
667
|
+
|
|
622
668
|
# ---- Events and webhooks (Event.ts, EventCatalog.ts, WebhookPayload.ts) ----------------------
|
|
623
669
|
|
|
624
670
|
# EVENT_TYPES in EventCatalog.ts. Ignore types your code does not handle. Webhook, Slack and email
|
|
@@ -661,7 +707,14 @@ module CalmLens
|
|
|
661
707
|
"createdAt" => Integer
|
|
662
708
|
}
|
|
663
709
|
# Unlike other lists, `nextCursor` is never nil: keep it to receive later events.
|
|
664
|
-
|
|
710
|
+
# `excludedTypes` are the event types the feed never includes, because the API key lacks the permission
|
|
711
|
+
# to see them.
|
|
712
|
+
type events_page = {
|
|
713
|
+
"items" => Array[event],
|
|
714
|
+
"nextCursor" => String,
|
|
715
|
+
"hasMore" => bool,
|
|
716
|
+
"excludedTypes" => Array[event_type]
|
|
717
|
+
}
|
|
665
718
|
|
|
666
719
|
# ---- Method options -------------------------------------------------------------------------
|
|
667
720
|
|
|
@@ -720,7 +773,7 @@ module CalmLens
|
|
|
720
773
|
# Defaults to `rendered` on the server; `original` takes no lens or transform.
|
|
721
774
|
?resource: "original" | "rendered",
|
|
722
775
|
?expires_in_seconds: Integer,
|
|
723
|
-
#
|
|
776
|
+
# Transform options replace the lens's options of the same name.
|
|
724
777
|
?lens: String,
|
|
725
778
|
?transform: transform_input,
|
|
726
779
|
?request_options: request_options
|
|
@@ -859,6 +912,37 @@ module CalmLens
|
|
|
859
912
|
?request_options: request_options }
|
|
860
913
|
type hash_list_entry_id_options = { entry_id: String, ?request_options: request_options }
|
|
861
914
|
|
|
915
|
+
# Policy settings as options: snake_case Symbol keys, or a policy's `settings` as the API returned it.
|
|
916
|
+
type policy_rule_input = { when: Array[String], action: flagged_content_action, ?name: String, ?match: "all", ?unless: Array[String] }
|
|
917
|
+
type category_policy_settings_input = {
|
|
918
|
+
?action: flagged_content_action,
|
|
919
|
+
?allowances: Array[policy_rule_input],
|
|
920
|
+
?restrictions: Array[policy_rule_input]
|
|
921
|
+
}
|
|
922
|
+
type link_safety_settings_input = {
|
|
923
|
+
verdict_actions: Hash[String | Symbol, link_safety_action],
|
|
924
|
+
?off_platform_links: { action: "limit" | "age-restrict" | "block", allowed_domains: Array[String] }
|
|
925
|
+
}
|
|
926
|
+
type policy_settings_input = policy_settings | {
|
|
927
|
+
category_settings: Hash[String | Symbol, category_policy_settings_input],
|
|
928
|
+
?link_safety: link_safety_settings_input
|
|
929
|
+
}
|
|
930
|
+
type list_policies_options = { ?request_options: request_options }
|
|
931
|
+
type policy_id_options = { policy_id: String, ?request_options: request_options }
|
|
932
|
+
# Send `settings` (defining every category) with a `name`, or `preset_id` to copy the latest
|
|
933
|
+
# release of a built-in preset, named after it unless you give a `name`.
|
|
934
|
+
type create_policy_options = { ?name: String, ?description: String, ?settings: policy_settings_input,
|
|
935
|
+
?preset_id: policy_preset_id, ?request_options: request_options }
|
|
936
|
+
type update_policy_options = { policy_id: String, revision: Integer, ?name: String, ?description: String,
|
|
937
|
+
?settings: policy_settings_input, ?request_options: request_options }
|
|
938
|
+
# `reset` restores the pinned preset version (send `preset.version`); `update` moves an unchanged
|
|
939
|
+
# copy to the latest release (send `preset.latestVersion`).
|
|
940
|
+
type apply_policy_preset_options = { policy_id: String, operation: "reset" | "update", revision: Integer,
|
|
941
|
+
preset_version: Integer, ?request_options: request_options }
|
|
942
|
+
type set_policy_automatic_updates_options = { policy_id: String, enabled: bool, revision: Integer,
|
|
943
|
+
preset_version: Integer, ?request_options: request_options }
|
|
944
|
+
type assign_policy_options = { policy_id: String, ?request_options: request_options }
|
|
945
|
+
|
|
862
946
|
# `page_size` is 1 to 100, default 50.
|
|
863
947
|
type list_events_options = { ?cursor: String, ?page_size: Integer, ?request_options: request_options }
|
|
864
948
|
|
|
@@ -870,7 +954,7 @@ module CalmLens
|
|
|
870
954
|
active: bool,
|
|
871
955
|
?description: String?,
|
|
872
956
|
?version: Integer,
|
|
873
|
-
?
|
|
957
|
+
?transform: image_transform_options_input?,
|
|
874
958
|
?request_options: request_options
|
|
875
959
|
}
|
|
876
960
|
|
|
@@ -881,7 +965,7 @@ module CalmLens
|
|
|
881
965
|
# `rendered` is the default. `original` takes no lens or transform; `placeholder` is a small blurred PNG
|
|
882
966
|
# that takes no lens or transform.
|
|
883
967
|
?resource: "original" | "rendered" | "placeholder",
|
|
884
|
-
#
|
|
968
|
+
# Transform options replace the lens's options of the same name.
|
|
885
969
|
?lens: String,
|
|
886
970
|
?transform: transform_input,
|
|
887
971
|
# Appends `?ageConfirmed=true`, so a public `age-restricted` asset is served from this URL. Set it
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: calmlens
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.20.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- CalmLens
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-11-
|
|
11
|
+
date: 2026-11-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: 'Server-side client for the CalmLens API: moderation, uploads, events,
|
|
14
14
|
webhooks and CDN delivery.'
|
|
@@ -30,6 +30,8 @@ files:
|
|
|
30
30
|
- lib/calmlens/internal.rb
|
|
31
31
|
- lib/calmlens/lenses.rb
|
|
32
32
|
- lib/calmlens/moderation_context.rb
|
|
33
|
+
- lib/calmlens/option_keys.rb
|
|
34
|
+
- lib/calmlens/policies.rb
|
|
33
35
|
- lib/calmlens/resource.rb
|
|
34
36
|
- lib/calmlens/response.rb
|
|
35
37
|
- lib/calmlens/server_client.rb
|
|
@@ -39,10 +41,16 @@ files:
|
|
|
39
41
|
- lib/calmlens/testing/fake_api.rb
|
|
40
42
|
- lib/calmlens/testing/mock_decision.rb
|
|
41
43
|
- lib/calmlens/testing/placeholder.rb
|
|
44
|
+
- lib/calmlens/testing/test_image_grid.rb
|
|
45
|
+
- lib/calmlens/testing/test_input_scenario.rb
|
|
46
|
+
- lib/calmlens/testing/test_inputs.rb
|
|
42
47
|
- lib/calmlens/text.rb
|
|
48
|
+
- lib/calmlens/transform_rules.rb
|
|
49
|
+
- lib/calmlens/transform_schema.rb
|
|
43
50
|
- lib/calmlens/transforms.rb
|
|
44
51
|
- lib/calmlens/transport.rb
|
|
45
52
|
- lib/calmlens/upload_body.rb
|
|
53
|
+
- lib/calmlens/upload_mime_types.rb
|
|
46
54
|
- lib/calmlens/upload_tokens.rb
|
|
47
55
|
- lib/calmlens/version.rb
|
|
48
56
|
- lib/calmlens/webhooks.rb
|