mlld 2.0.4 → 2.0.5

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 (4) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -0
  3. data/lib/mlld.rb +59 -6
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36d855d7e5fa4638ceda66f9e7c7bde1edc7c0939d54a6dbe690bcc8435e81f8
4
- data.tar.gz: d26e2fa2a32b69a325d859baec356331e01b7550e009b233186fa83bde0c1614
3
+ metadata.gz: ff041879477a16d48049a2d8e1d220960c88622f6d544db8b00af88ec4952707
4
+ data.tar.gz: a5b86461dc631d729c50e58822374bc0e08669ae98c4f96665febd9aa01ef449
5
5
  SHA512:
6
- metadata.gz: 97cb037150d27a07589ac05a230470cb76f8a703b17e3b6efcc476446eee4ad02c2a8374f1a9fae6288477688587a11c909a00576cc0d13ebc0e55313a9be4fc
7
- data.tar.gz: 2190023a9b5dbdab5c5752f4abbaec07e55d5fa614efb3182d2046d249ff2cb0023faa63889788277ca35e482b24d5808108d932e4695179ea6989e7b9fbddd7
6
+ metadata.gz: 819fbc89a98bdccffc5a012ece626f339caeb27b1143364dfcfe60e87670f320e2e7f1afc28aa1795584fab78a7f8bf73b4788490dfa845cf11c8ab34f7ea614
7
+ data.tar.gz: 79c752b1c2e328cb0f7f31330751764119a6bff7d5b4ec7839d06d57d6be024a7a84cfb84c6440d16a0a530a29a2389a498e8493211d1ae0b48d64568bed7fd8
data/README.md CHANGED
@@ -94,3 +94,4 @@ puts handle.result
94
94
 
95
95
  - Each `Client` keeps one live RPC subprocess for repeated calls.
96
96
  - `ExecuteResult.state_writes` merges final-result writes and streamed `state:write` events.
97
+ - `ExecuteResult.denials` collects structured guard/policy label-flow denials observed during execution.
data/lib/mlld.rb CHANGED
@@ -19,7 +19,8 @@ module Mlld
19
19
  StateWrite = Struct.new(:path, :value, :timestamp, keyword_init: true)
20
20
  Metrics = Struct.new(:total_ms, :parse_ms, :evaluate_ms, keyword_init: true)
21
21
  Effect = Struct.new(:type, :content, :security, keyword_init: true)
22
- ExecuteResult = Struct.new(:output, :state_writes, :exports, :effects, :metrics, keyword_init: true)
22
+ GuardDenial = Struct.new(:guard, :operation, :reason, :rule, :labels, :args, keyword_init: true)
23
+ ExecuteResult = Struct.new(:output, :state_writes, :exports, :effects, :denials, :metrics, keyword_init: true)
23
24
 
24
25
  Executable = Struct.new(:name, :params, :labels, keyword_init: true)
25
26
  Import = Struct.new(:from, :names, keyword_init: true)
@@ -57,8 +58,8 @@ module Mlld
57
58
  @client.send_cancel(@request_id)
58
59
  end
59
60
 
60
- def update_state(path, value, timeout: nil)
61
- @client.send_state_update(@request_id, path, value, timeout || @timeout)
61
+ def update_state(path, value, labels: nil, timeout: nil)
62
+ @client.send_state_update(@request_id, path, value, timeout || @timeout, labels: labels)
62
63
  end
63
64
 
64
65
  protected
@@ -200,6 +201,7 @@ module Mlld
200
201
  script,
201
202
  file_path: nil,
202
203
  payload: nil,
204
+ payload_labels: nil,
203
205
  state: nil,
204
206
  dynamic_modules: nil,
205
207
  dynamic_module_source: nil,
@@ -211,6 +213,7 @@ module Mlld
211
213
  script,
212
214
  file_path: file_path,
213
215
  payload: payload,
216
+ payload_labels: payload_labels,
214
217
  state: state,
215
218
  dynamic_modules: dynamic_modules,
216
219
  dynamic_module_source: dynamic_module_source,
@@ -224,6 +227,7 @@ module Mlld
224
227
  script,
225
228
  file_path: nil,
226
229
  payload: nil,
230
+ payload_labels: nil,
227
231
  state: nil,
228
232
  dynamic_modules: nil,
229
233
  dynamic_module_source: nil,
@@ -234,6 +238,8 @@ module Mlld
234
238
  params = { 'script' => script }
235
239
  params['filePath'] = file_path if file_path
236
240
  params['payload'] = payload unless payload.nil?
241
+ normalized_payload_labels = normalize_payload_labels(payload_labels)
242
+ params['payloadLabels'] = normalized_payload_labels if normalized_payload_labels
237
243
  params['state'] = state if state
238
244
  params['dynamicModules'] = dynamic_modules if dynamic_modules
239
245
  params['dynamicModuleSource'] = dynamic_module_source if dynamic_module_source
@@ -252,6 +258,7 @@ module Mlld
252
258
  def execute(
253
259
  filepath,
254
260
  payload = nil,
261
+ payload_labels: nil,
255
262
  state: nil,
256
263
  dynamic_modules: nil,
257
264
  dynamic_module_source: nil,
@@ -262,6 +269,7 @@ module Mlld
262
269
  execute_async(
263
270
  filepath,
264
271
  payload,
272
+ payload_labels: payload_labels,
265
273
  state: state,
266
274
  dynamic_modules: dynamic_modules,
267
275
  dynamic_module_source: dynamic_module_source,
@@ -274,6 +282,7 @@ module Mlld
274
282
  def execute_async(
275
283
  filepath,
276
284
  payload = nil,
285
+ payload_labels: nil,
277
286
  state: nil,
278
287
  dynamic_modules: nil,
279
288
  dynamic_module_source: nil,
@@ -283,6 +292,8 @@ module Mlld
283
292
  )
284
293
  params = { 'filepath' => filepath }
285
294
  params['payload'] = payload unless payload.nil?
295
+ normalized_payload_labels = normalize_payload_labels(payload_labels)
296
+ params['payloadLabels'] = normalized_payload_labels if normalized_payload_labels
286
297
  params['state'] = state if state
287
298
  params['dynamicModules'] = dynamic_modules if dynamic_modules
288
299
  params['dynamicModuleSource'] = dynamic_module_source if dynamic_module_source
@@ -309,7 +320,7 @@ module Mlld
309
320
  nil
310
321
  end
311
322
 
312
- def send_state_update(request_id, path, value, timeout)
323
+ def send_state_update(request_id, path, value, timeout, labels: nil)
313
324
  unless path.is_a?(String) && !path.strip.empty?
314
325
  raise Error.new('state update path is required', code: 'INVALID_REQUEST')
315
326
  end
@@ -317,14 +328,17 @@ module Mlld
317
328
  resolved_timeout = resolve_timeout(timeout)
318
329
  max_wait = resolved_timeout || 2.0
319
330
  deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + max_wait
331
+ normalized_labels = normalize_label_list(labels)
320
332
 
321
333
  loop do
322
334
  begin
323
- request('state:update', {
335
+ params = {
324
336
  'requestId' => request_id,
325
337
  'path' => path,
326
338
  'value' => value
327
- }, resolved_timeout)
339
+ }
340
+ params['labels'] = normalized_labels if normalized_labels
341
+ request('state:update', params, resolved_timeout)
328
342
  return nil
329
343
  rescue Error => error
330
344
  raise unless error.code == 'REQUEST_NOT_FOUND'
@@ -335,6 +349,31 @@ module Mlld
335
349
  end
336
350
  end
337
351
 
352
+ def normalize_payload_labels(payload_labels)
353
+ return nil if payload_labels.nil?
354
+ return nil unless payload_labels.is_a?(Hash)
355
+
356
+ normalized = {}
357
+ payload_labels.each do |key, labels|
358
+ deduped = normalize_label_list(labels)
359
+ normalized[key] = deduped if deduped
360
+ end
361
+ normalized.empty? ? nil : normalized
362
+ end
363
+
364
+ def normalize_label_list(labels)
365
+ return nil if labels.nil?
366
+
367
+ raw = labels.is_a?(Array) ? labels : [labels]
368
+ normalized = raw
369
+ .select { |label| label.is_a?(String) }
370
+ .map(&:strip)
371
+ .reject(&:empty?)
372
+ .uniq
373
+
374
+ normalized.empty? ? nil : normalized
375
+ end
376
+
338
377
  def await_request(request_id, response_queue, timeout)
339
378
  state_write_events = []
340
379
  deadline = timeout ? Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout : nil
@@ -412,11 +451,25 @@ module Mlld
412
451
  )
413
452
  end.compact
414
453
 
454
+ denials = Array(result['denials']).map do |entry|
455
+ next unless entry.is_a?(Hash)
456
+
457
+ GuardDenial.new(
458
+ guard: entry['guard'],
459
+ operation: entry['operation'].to_s,
460
+ reason: entry['reason'].to_s,
461
+ rule: entry['rule'],
462
+ labels: Array(entry['labels']).select { |label| label.is_a?(String) },
463
+ args: entry['args'].is_a?(Hash) ? entry['args'] : nil
464
+ )
465
+ end.compact
466
+
415
467
  ExecuteResult.new(
416
468
  output: result['output'].to_s,
417
469
  state_writes: state_writes,
418
470
  exports: result.fetch('exports', []),
419
471
  effects: effects,
472
+ denials: denials,
420
473
  metrics: metrics
421
474
  )
422
475
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mlld
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.4
4
+ version: 2.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - mlld-lang
@@ -39,7 +39,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  requirements: []
42
- rubygems_version: 4.0.3
42
+ rubygems_version: 4.0.6
43
43
  specification_version: 4
44
44
  summary: Ruby wrapper for the mlld CLI
45
45
  test_files: []