ast-template 7.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5711275307f85c18f6da22576ff9993c315c34da2c52027c1ddeb084f5956d2f
4
+ data.tar.gz: 5a0c44198448950f63c7b9340aaa9dc9b482a0686058ec2cdedde6ba1f3f2f0d
5
+ SHA512:
6
+ metadata.gz: bb7bb8d23ae8116b90bde1ab0c88893a5705ffc5ffcc1bc13a1f10f621c1123f08267f73e0481ece16d0d49499aeae0befaa3c879cf42e43babd79e997222de9
7
+ data.tar.gz: 1aab9144fd5a9508257fb1f4a605f602b4907b59bffb64c6a6212c72eea80968cac8c315aeea8a836b3c9cd05112f9ad3ed068ea93c2b37ddb3b0be7b30f11f5
checksums.yaml.gz.sig ADDED
Binary file
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ast
4
+ module Template
5
+ module Version
6
+ VERSION = "7.0.0"
7
+ end
8
+
9
+ VERSION = Version::VERSION
10
+ end
11
+ end
@@ -0,0 +1,1211 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ast/merge"
4
+ require_relative "template/version"
5
+
6
+ module Ast
7
+ module Template
8
+ MODES = %w[plan apply reapply].freeze
9
+ SESSION_STATUS_TRANSPORT_VERSION = 1
10
+ SESSION_DIAGNOSTICS_TRANSPORT_VERSION = 1
11
+ SESSION_OUTCOME_TRANSPORT_VERSION = 1
12
+ SESSION_INSPECTION_TRANSPORT_VERSION = 1
13
+ SESSION_REQUEST_TRANSPORT_VERSION = 1
14
+ SESSION_RUNNER_REQUEST_TRANSPORT_VERSION = 1
15
+ SESSION_RUNNER_PAYLOAD_TRANSPORT_VERSION = 1
16
+ SESSION_ENTRYPOINT_TRANSPORT_VERSION = 1
17
+ SESSION_COMMAND_TRANSPORT_VERSION = 1
18
+ SESSION_COMMAND_PAYLOAD_TRANSPORT_VERSION = 1
19
+ SESSION_INVOCATION_TRANSPORT_VERSION = 1
20
+
21
+ class << self
22
+ def merge_prepared_content_from_registry(registry, entry)
23
+ family = entry.dig(:classification, :family) || entry.dig("classification", "family")
24
+ adapter = registry[family.to_s]
25
+ unless adapter
26
+ return {
27
+ ok: false,
28
+ diagnostics: [{
29
+ severity: "error",
30
+ category: "configuration_error",
31
+ message: "missing family adapter for #{family}"
32
+ }],
33
+ policies: []
34
+ }
35
+ end
36
+
37
+ adapter.call(deep_dup(entry))
38
+ end
39
+
40
+ def registered_adapter_families(registry)
41
+ registry.keys.map(&:to_s).sort
42
+ end
43
+
44
+ def report_template_directory_registry_session(mode, entries, registry, result = nil)
45
+ normalized_mode = mode.to_s
46
+ raise ArgumentError, "unsupported template session mode: #{mode}" unless MODES.include?(normalized_mode)
47
+
48
+ {
49
+ mode: normalized_mode,
50
+ adapter_families: registered_adapter_families(registry),
51
+ diagnostics: Array(result&.dig(:apply_result, :diagnostics) || result&.dig("apply_result", "diagnostics")),
52
+ runner_report: Ast::Merge.report_template_directory_runner(entries, result)
53
+ }
54
+ end
55
+
56
+ def default_family_merge_adapter_registry(allowed_families = nil)
57
+ allowed = Array(allowed_families).map(&:to_s)
58
+ include_family = lambda do |family|
59
+ allowed.empty? || allowed.include?(family)
60
+ end
61
+
62
+ registry = {}
63
+ if include_family.call("markdown")
64
+ begin
65
+ require "markdown-merge"
66
+ registry["markdown"] = lambda do |entry|
67
+ Markdown::Merge.merge_markdown(entry[:prepared_template_content], entry[:destination_content], "markdown")
68
+ end
69
+ rescue LoadError
70
+ end
71
+ end
72
+ if include_family.call("toml")
73
+ begin
74
+ require "toml-merge"
75
+ registry["toml"] = lambda do |entry|
76
+ Toml::Merge.merge_toml(entry[:prepared_template_content], entry[:destination_content], "toml")
77
+ end
78
+ rescue LoadError
79
+ end
80
+ end
81
+ if include_family.call("ruby")
82
+ begin
83
+ require "ruby-merge"
84
+ registry["ruby"] = lambda do |entry|
85
+ Ruby::Merge.merge_ruby(entry[:prepared_template_content], entry[:destination_content], "ruby")
86
+ end
87
+ rescue LoadError
88
+ end
89
+ end
90
+
91
+ registry
92
+ end
93
+
94
+ def report_template_directory_session(mode, entries, result = nil)
95
+ normalized_mode = mode.to_s
96
+ raise ArgumentError, "unsupported template session mode: #{mode}" unless MODES.include?(normalized_mode)
97
+
98
+ {
99
+ mode: normalized_mode,
100
+ runner_report: Ast::Merge.report_template_directory_runner(entries, result)
101
+ }
102
+ end
103
+
104
+ def plan_template_directory_session_from_directories(template_root, destination_root,
105
+ context, default_strategy, overrides, replacements, config = nil)
106
+ entries = Ast::Merge.plan_template_tree_execution_from_directories(
107
+ template_root,
108
+ destination_root,
109
+ context,
110
+ default_strategy,
111
+ overrides,
112
+ replacements,
113
+ config
114
+ )
115
+ report_template_directory_session(:plan, entries)
116
+ end
117
+
118
+ def apply_template_directory_session_to_directory(template_root, destination_root,
119
+ context, default_strategy, overrides, replacements, config = nil, &merge_callback)
120
+ result = Ast::Merge.apply_template_tree_execution_to_directory(
121
+ template_root,
122
+ destination_root,
123
+ context,
124
+ default_strategy,
125
+ overrides,
126
+ replacements,
127
+ config,
128
+ &merge_callback
129
+ )
130
+ report_template_directory_session(:apply, result[:execution_plan], result)
131
+ end
132
+
133
+ def reapply_template_directory_session_to_directory(template_root, destination_root,
134
+ context, default_strategy, overrides, replacements, config = nil, &merge_callback)
135
+ result = Ast::Merge.apply_template_tree_execution_to_directory(
136
+ template_root,
137
+ destination_root,
138
+ context,
139
+ default_strategy,
140
+ overrides,
141
+ replacements,
142
+ config,
143
+ &merge_callback
144
+ )
145
+ report_template_directory_session(:reapply, result[:execution_plan], result)
146
+ end
147
+
148
+ def apply_template_directory_session_with_registry_to_directory(template_root, destination_root,
149
+ context, default_strategy, overrides, replacements, registry, config = nil)
150
+ result = Ast::Merge.apply_template_tree_execution_to_directory(
151
+ template_root,
152
+ destination_root,
153
+ context,
154
+ default_strategy,
155
+ overrides,
156
+ replacements,
157
+ config
158
+ ) do |entry|
159
+ merge_prepared_content_from_registry(registry, entry)
160
+ end
161
+ report_template_directory_registry_session(:apply, result[:execution_plan], registry, result)
162
+ end
163
+
164
+ def apply_template_directory_session_with_default_registry_to_directory(template_root, destination_root,
165
+ context, default_strategy, overrides, replacements, allowed_families = nil, config = nil)
166
+ registry = default_family_merge_adapter_registry(allowed_families)
167
+ apply_template_directory_session_with_registry_to_directory(
168
+ template_root,
169
+ destination_root,
170
+ context,
171
+ default_strategy,
172
+ overrides,
173
+ replacements,
174
+ registry,
175
+ config
176
+ )
177
+ end
178
+
179
+ def required_families(entries)
180
+ Array(entries).filter_map do |entry|
181
+ next unless (entry[:execution_action] || entry["execution_action"]).to_s == "merge_prepared_content"
182
+
183
+ entry.dig(:classification, :family) || entry.dig("classification", "family")
184
+ end.uniq.sort
185
+ end
186
+
187
+ def report_adapter_capabilities(entries, registry)
188
+ available = registered_adapter_families(registry)
189
+ required = required_families(entries)
190
+ missing = required - available
191
+ {
192
+ required_families: required,
193
+ adapter_families: available,
194
+ missing_families: missing,
195
+ ready: missing.empty?
196
+ }
197
+ end
198
+
199
+ def report_adapter_capabilities_from_directories(template_root, destination_root,
200
+ context, default_strategy, overrides, replacements, registry, config = nil)
201
+ entries = Ast::Merge.plan_template_tree_execution_from_directories(
202
+ template_root,
203
+ destination_root,
204
+ context,
205
+ default_strategy,
206
+ overrides,
207
+ replacements,
208
+ config
209
+ )
210
+ report_adapter_capabilities(entries, registry)
211
+ end
212
+
213
+ def report_default_adapter_capabilities_from_directories(template_root, destination_root,
214
+ context, default_strategy, overrides, replacements, allowed_families = nil, config = nil)
215
+ report_adapter_capabilities_from_directories(
216
+ template_root,
217
+ destination_root,
218
+ context,
219
+ default_strategy,
220
+ overrides,
221
+ replacements,
222
+ default_family_merge_adapter_registry(allowed_families),
223
+ config
224
+ )
225
+ end
226
+
227
+ def report_template_directory_session_envelope(session_report, adapter_capabilities)
228
+ {
229
+ session_report: session_report,
230
+ adapter_capabilities: adapter_capabilities
231
+ }
232
+ end
233
+
234
+ def plan_template_directory_session_envelope_from_directories(template_root, destination_root,
235
+ context, default_strategy, overrides, replacements, allowed_families = nil, config = nil)
236
+ report_template_directory_session_envelope(
237
+ plan_template_directory_session_from_directories(
238
+ template_root,
239
+ destination_root,
240
+ context,
241
+ default_strategy,
242
+ overrides,
243
+ replacements,
244
+ config
245
+ ),
246
+ report_default_adapter_capabilities_from_directories(
247
+ template_root,
248
+ destination_root,
249
+ context,
250
+ default_strategy,
251
+ overrides,
252
+ replacements,
253
+ allowed_families,
254
+ config
255
+ )
256
+ )
257
+ end
258
+
259
+ def apply_template_directory_session_envelope_with_default_registry_to_directory(template_root, destination_root,
260
+ context, default_strategy, overrides, replacements, allowed_families = nil, config = nil)
261
+ report_template_directory_session_envelope(
262
+ apply_template_directory_session_with_default_registry_to_directory(
263
+ template_root,
264
+ destination_root,
265
+ context,
266
+ default_strategy,
267
+ overrides,
268
+ replacements,
269
+ allowed_families,
270
+ config
271
+ ),
272
+ report_default_adapter_capabilities_from_directories(
273
+ template_root,
274
+ destination_root,
275
+ context,
276
+ default_strategy,
277
+ overrides,
278
+ replacements,
279
+ allowed_families,
280
+ config
281
+ )
282
+ )
283
+ end
284
+
285
+ def report_template_directory_session_status(envelope)
286
+ session_report = envelope[:session_report] || envelope["session_report"] || {}
287
+ adapter_capabilities = envelope[:adapter_capabilities] || envelope["adapter_capabilities"] || {}
288
+ runner_report = session_report[:runner_report] || session_report["runner_report"] || {}
289
+ plan_report = runner_report[:plan_report] || runner_report["plan_report"] || {}
290
+ entries = Array(plan_report[:entries] || plan_report["entries"])
291
+ plan_summary = plan_report[:summary] || plan_report["summary"] || {}
292
+ apply_report = runner_report[:apply_report] || runner_report["apply_report"] || {}
293
+ apply_entries = Array(apply_report[:entries] || apply_report["entries"])
294
+ apply_summary = apply_report[:summary] || apply_report["summary"] || {}
295
+ blocked_paths = (entries + apply_entries).filter_map do |entry|
296
+ status = entry[:status] || entry["status"]
297
+ destination_path = entry[:destination_path] || entry["destination_path"]
298
+ destination_path if status.to_s == "blocked" && destination_path
299
+ end.uniq.sort
300
+ missing_families = Array(
301
+ adapter_capabilities[:missing_families] || adapter_capabilities["missing_families"]
302
+ ).map(&:to_s).sort
303
+
304
+ {
305
+ mode: (session_report[:mode] || session_report["mode"]).to_s,
306
+ ready: !!(adapter_capabilities[:ready] || adapter_capabilities["ready"]) && blocked_paths.empty?,
307
+ missing_families: missing_families,
308
+ blocked_paths: blocked_paths,
309
+ planned_write_count: plan_summary.fetch(:create, plan_summary.fetch("create", 0)) +
310
+ plan_summary.fetch(:update, plan_summary.fetch("update", 0)),
311
+ written_count: apply_summary.fetch(:written, apply_summary.fetch("written", 0))
312
+ }
313
+ end
314
+
315
+ def template_directory_session_status_envelope(status)
316
+ {
317
+ kind: "template_directory_session_status",
318
+ version: SESSION_STATUS_TRANSPORT_VERSION,
319
+ status: deep_dup(status)
320
+ }
321
+ end
322
+
323
+ def import_template_directory_session_status_envelope(envelope)
324
+ return [nil, { category: "kind_mismatch", message: "expected template_directory_session_status envelope kind." }] unless envelope[:kind] == "template_directory_session_status"
325
+ return [nil, { category: "unsupported_version", message: "unsupported template_directory_session_status envelope version #{envelope[:version]}." }] unless envelope[:version] == SESSION_STATUS_TRANSPORT_VERSION
326
+
327
+ [deep_dup(envelope[:status]), nil]
328
+ end
329
+
330
+ def report_template_directory_session_diagnostics(mode, entries, adapter_capabilities, result = nil)
331
+ missing_families = Array(
332
+ adapter_capabilities[:missing_families] || adapter_capabilities["missing_families"]
333
+ ).map(&:to_s)
334
+ blocked_apply_paths = Array(
335
+ result&.dig(:apply_report, :entries) || result&.dig("apply_report", "entries")
336
+ ).filter_map do |entry|
337
+ destination_path = entry[:destination_path] || entry["destination_path"]
338
+ status = entry[:status] || entry["status"]
339
+ destination_path if status.to_s == "blocked" && destination_path
340
+ end
341
+
342
+ diagnostics = Array(entries).flat_map do |entry|
343
+ path = entry[:destination_path] || entry["destination_path"] ||
344
+ entry[:logical_destination_path] || entry["logical_destination_path"]
345
+ family = entry.dig(:classification, :family) || entry.dig("classification", "family")
346
+ result_entries = []
347
+ block_reason = entry[:block_reason] || entry["block_reason"]
348
+ if (entry[:blocked] || entry["blocked"]) && block_reason.to_s == "unresolved_tokens"
349
+ result_entries << {
350
+ severity: "error",
351
+ category: "configuration_error",
352
+ reason: "unresolved_tokens",
353
+ path: path,
354
+ message: "unresolved template tokens block #{path}"
355
+ }
356
+ end
357
+ if (entry[:execution_action] || entry["execution_action"]).to_s == "merge_prepared_content" &&
358
+ missing_families.include?(family.to_s) &&
359
+ (result.nil? || blocked_apply_paths.empty? || blocked_apply_paths.include?(path))
360
+ result_entries << {
361
+ severity: "error",
362
+ category: "configuration_error",
363
+ reason: "missing_family_adapter",
364
+ path: path,
365
+ family: family.to_s,
366
+ message: "missing family adapter for #{family} blocks #{path}"
367
+ }
368
+ end
369
+ result_entries
370
+ end.sort_by { |entry| [entry[:path].to_s, entry[:reason].to_s, entry[:family].to_s] }
371
+
372
+ {
373
+ mode: mode.to_s,
374
+ ready: diagnostics.empty?,
375
+ diagnostics: diagnostics
376
+ }
377
+ end
378
+
379
+ def template_directory_session_diagnostics_envelope(diagnostics)
380
+ {
381
+ kind: "template_directory_session_diagnostics",
382
+ version: SESSION_DIAGNOSTICS_TRANSPORT_VERSION,
383
+ diagnostics: deep_dup(diagnostics)
384
+ }
385
+ end
386
+
387
+ def import_template_directory_session_diagnostics_envelope(envelope)
388
+ return [nil, { category: "kind_mismatch", message: "expected template_directory_session_diagnostics envelope kind." }] unless envelope[:kind] == "template_directory_session_diagnostics"
389
+ return [nil, { category: "unsupported_version", message: "unsupported template_directory_session_diagnostics envelope version #{envelope[:version]}." }] unless envelope[:version] == SESSION_DIAGNOSTICS_TRANSPORT_VERSION
390
+
391
+ [deep_dup(envelope[:diagnostics]), nil]
392
+ end
393
+
394
+ def plan_template_directory_session_diagnostics_from_directories(template_root, destination_root,
395
+ context, default_strategy, overrides, replacements, allowed_families = nil, config = nil)
396
+ entries = Ast::Merge.plan_template_tree_execution_from_directories(
397
+ template_root,
398
+ destination_root,
399
+ context,
400
+ default_strategy,
401
+ overrides,
402
+ replacements,
403
+ config
404
+ )
405
+ report_template_directory_session_diagnostics(
406
+ :plan,
407
+ entries,
408
+ report_adapter_capabilities(entries, default_family_merge_adapter_registry(allowed_families))
409
+ )
410
+ end
411
+
412
+ def apply_template_directory_session_diagnostics_with_default_registry_to_directory(template_root, destination_root,
413
+ context, default_strategy, overrides, replacements, allowed_families = nil, config = nil)
414
+ registry = default_family_merge_adapter_registry(allowed_families)
415
+ result = Ast::Merge.apply_template_tree_execution_to_directory(
416
+ template_root,
417
+ destination_root,
418
+ context,
419
+ default_strategy,
420
+ overrides,
421
+ replacements,
422
+ config
423
+ ) do |entry|
424
+ merge_prepared_content_from_registry(registry, entry)
425
+ end
426
+ report_template_directory_session_diagnostics(
427
+ :apply,
428
+ result[:execution_plan],
429
+ report_adapter_capabilities(result[:execution_plan], registry),
430
+ result
431
+ )
432
+ end
433
+
434
+ def report_template_directory_session_outcome(session_report, status, diagnostics)
435
+ {
436
+ session_report: session_report,
437
+ status: status,
438
+ diagnostics: diagnostics
439
+ }
440
+ end
441
+
442
+ def plan_template_directory_session_outcome_from_directories(template_root, destination_root,
443
+ context, default_strategy, overrides, replacements, allowed_families = nil, config = nil)
444
+ report_template_directory_session_outcome(
445
+ plan_template_directory_session_from_directories(
446
+ template_root,
447
+ destination_root,
448
+ context,
449
+ default_strategy,
450
+ overrides,
451
+ replacements,
452
+ config
453
+ ),
454
+ report_template_directory_session_status(
455
+ plan_template_directory_session_envelope_from_directories(
456
+ template_root,
457
+ destination_root,
458
+ context,
459
+ default_strategy,
460
+ overrides,
461
+ replacements,
462
+ allowed_families,
463
+ config
464
+ )
465
+ ),
466
+ plan_template_directory_session_diagnostics_from_directories(
467
+ template_root,
468
+ destination_root,
469
+ context,
470
+ default_strategy,
471
+ overrides,
472
+ replacements,
473
+ allowed_families,
474
+ config
475
+ )
476
+ )
477
+ end
478
+
479
+ def apply_template_directory_session_outcome_with_default_registry_to_directory(template_root, destination_root,
480
+ context, default_strategy, overrides, replacements, allowed_families = nil, config = nil)
481
+ registry = default_family_merge_adapter_registry(allowed_families)
482
+ result = Ast::Merge.apply_template_tree_execution_to_directory(
483
+ template_root,
484
+ destination_root,
485
+ context,
486
+ default_strategy,
487
+ overrides,
488
+ replacements,
489
+ config
490
+ ) do |entry|
491
+ merge_prepared_content_from_registry(registry, entry)
492
+ end
493
+ session_report = report_template_directory_registry_session(:apply, result[:execution_plan], registry, result)
494
+ capabilities = report_adapter_capabilities(result[:execution_plan], registry)
495
+ report_template_directory_session_outcome(
496
+ session_report,
497
+ report_template_directory_session_status(
498
+ report_template_directory_session_envelope(session_report, capabilities)
499
+ ),
500
+ report_template_directory_session_diagnostics(
501
+ :apply,
502
+ result[:execution_plan],
503
+ capabilities,
504
+ result
505
+ )
506
+ )
507
+ end
508
+
509
+ def reapply_template_directory_session_outcome_with_default_registry_to_directory(template_root, destination_root,
510
+ context, default_strategy, overrides, replacements, allowed_families = nil, config = nil)
511
+ registry = default_family_merge_adapter_registry(allowed_families)
512
+ result = Ast::Merge.apply_template_tree_execution_to_directory(
513
+ template_root,
514
+ destination_root,
515
+ context,
516
+ default_strategy,
517
+ overrides,
518
+ replacements,
519
+ config
520
+ ) do |entry|
521
+ merge_prepared_content_from_registry(registry, entry)
522
+ end
523
+ session_report = report_template_directory_registry_session(:reapply, result[:execution_plan], registry, result)
524
+ capabilities = report_adapter_capabilities(result[:execution_plan], registry)
525
+ report_template_directory_session_outcome(
526
+ session_report,
527
+ report_template_directory_session_status(
528
+ report_template_directory_session_envelope(session_report, capabilities)
529
+ ),
530
+ report_template_directory_session_diagnostics(
531
+ :reapply,
532
+ result[:execution_plan],
533
+ capabilities,
534
+ result
535
+ )
536
+ )
537
+ end
538
+
539
+ def run_template_directory_session_with_default_registry_to_directory(mode, template_root, destination_root,
540
+ context, default_strategy, overrides, replacements, allowed_families = nil, config = nil)
541
+ case mode.to_s
542
+ when "plan"
543
+ plan_template_directory_session_outcome_from_directories(
544
+ template_root,
545
+ destination_root,
546
+ context,
547
+ default_strategy,
548
+ overrides,
549
+ replacements,
550
+ allowed_families,
551
+ config
552
+ )
553
+ when "apply"
554
+ apply_template_directory_session_outcome_with_default_registry_to_directory(
555
+ template_root,
556
+ destination_root,
557
+ context,
558
+ default_strategy,
559
+ overrides,
560
+ replacements,
561
+ allowed_families,
562
+ config
563
+ )
564
+ when "reapply"
565
+ reapply_template_directory_session_outcome_with_default_registry_to_directory(
566
+ template_root,
567
+ destination_root,
568
+ context,
569
+ default_strategy,
570
+ overrides,
571
+ replacements,
572
+ allowed_families,
573
+ config
574
+ )
575
+ else
576
+ raise ArgumentError, "unsupported template session mode: #{mode}"
577
+ end
578
+ end
579
+
580
+ def run_template_directory_session_with_options(options)
581
+ request = report_template_directory_session_options_request(options)
582
+ unless request[:ready]
583
+ return report_template_directory_session_configuration_outcome(
584
+ request[:mode],
585
+ { mode: request[:mode], ready: request[:ready], diagnostics: request[:diagnostics] }
586
+ )
587
+ end
588
+ normalized = deep_dup(request[:resolved_options])
589
+ run_template_directory_session_with_default_registry_to_directory(
590
+ normalized[:mode] || normalized["mode"],
591
+ normalized[:template_root] || normalized["template_root"],
592
+ normalized[:destination_root] || normalized["destination_root"],
593
+ normalized[:context] || normalized["context"] || {},
594
+ normalized[:default_strategy] || normalized["default_strategy"],
595
+ normalized[:overrides] || normalized["overrides"] || [],
596
+ normalized[:replacements] || normalized["replacements"] || {},
597
+ normalized[:allowed_families] || normalized["allowed_families"],
598
+ normalized[:config] || normalized["config"]
599
+ )
600
+ end
601
+
602
+ def report_template_directory_session_options_configuration(options)
603
+ normalized = deep_dup(options)
604
+ diagnostics = []
605
+ unless (normalized[:destination_root] || normalized["destination_root"]).to_s.length.positive?
606
+ diagnostics << {
607
+ severity: "error",
608
+ category: "configuration_error",
609
+ reason: "missing_destination_root",
610
+ message: "missing destination_root for template session"
611
+ }
612
+ end
613
+ unless (normalized[:template_root] || normalized["template_root"]).to_s.length.positive?
614
+ diagnostics << {
615
+ severity: "error",
616
+ category: "configuration_error",
617
+ reason: "missing_template_root",
618
+ message: "missing template_root for template session"
619
+ }
620
+ end
621
+ diagnostics.sort_by! { |entry| entry[:reason] }
622
+ {
623
+ mode: normalize_session_mode(normalized[:mode] || normalized["mode"]),
624
+ ready: diagnostics.empty?,
625
+ diagnostics: diagnostics
626
+ }
627
+ end
628
+
629
+ def report_template_directory_session_options_request(options)
630
+ normalized = deep_dup(options)
631
+ configuration = report_template_directory_session_options_configuration(normalized)
632
+ {
633
+ request_kind: "options",
634
+ mode: configuration[:mode],
635
+ ready: configuration[:ready],
636
+ diagnostics: deep_dup(configuration[:diagnostics]),
637
+ resolved_options: configuration[:ready] ? compact_session_request_options(normalized) : nil
638
+ }
639
+ end
640
+
641
+ def report_template_directory_session_profile_configuration(profiles, profile_name, overrides)
642
+ normalized_profiles = deep_dup(profiles)
643
+ normalized_overrides = deep_dup(overrides)
644
+ diagnostics = report_template_directory_session_options_configuration(normalized_overrides)[:diagnostics]
645
+ profile = normalized_profiles[profile_name.to_s] || normalized_profiles[profile_name.to_sym]
646
+ mode = normalize_session_mode(
647
+ normalized_overrides[:mode] || normalized_overrides["mode"] || profile&.[](:mode) || profile&.[]("mode")
648
+ )
649
+ unless profile
650
+ diagnostics << {
651
+ severity: "error",
652
+ category: "configuration_error",
653
+ reason: "missing_profile",
654
+ message: "unknown template session profile: #{profile_name}"
655
+ }
656
+ end
657
+ diagnostics.sort_by! { |entry| entry[:reason] }
658
+ {
659
+ mode: mode,
660
+ ready: diagnostics.empty?,
661
+ diagnostics: diagnostics
662
+ }
663
+ end
664
+
665
+ def report_template_directory_session_profile_request(profiles, profile_name, overrides)
666
+ configuration = report_template_directory_session_profile_configuration(profiles, profile_name, overrides)
667
+ {
668
+ request_kind: "profile",
669
+ profile_name: profile_name.to_s,
670
+ mode: configuration[:mode],
671
+ ready: configuration[:ready],
672
+ diagnostics: deep_dup(configuration[:diagnostics]),
673
+ resolved_options: configuration[:ready] ? compact_session_request_options(
674
+ resolve_template_directory_session_options(profiles, profile_name, overrides)
675
+ ) : nil
676
+ }
677
+ end
678
+
679
+ def report_template_directory_session_configuration_outcome(mode, diagnostics)
680
+ report_template_directory_session_outcome(
681
+ report_template_directory_session(mode, []),
682
+ {
683
+ mode: mode,
684
+ ready: false,
685
+ missing_families: [],
686
+ blocked_paths: [],
687
+ planned_write_count: 0,
688
+ written_count: 0
689
+ },
690
+ diagnostics
691
+ )
692
+ end
693
+
694
+ def template_directory_session_outcome_envelope(outcome)
695
+ {
696
+ kind: "template_directory_session_outcome",
697
+ version: SESSION_OUTCOME_TRANSPORT_VERSION,
698
+ outcome: deep_dup(outcome)
699
+ }
700
+ end
701
+
702
+ def import_template_directory_session_outcome_envelope(envelope)
703
+ return [nil, { category: "kind_mismatch", message: "expected template_directory_session_outcome envelope kind." }] unless envelope[:kind] == "template_directory_session_outcome"
704
+ return [nil, { category: "unsupported_version", message: "unsupported template_directory_session_outcome envelope version #{envelope[:version]}." }] unless envelope[:version] == SESSION_OUTCOME_TRANSPORT_VERSION
705
+
706
+ [deep_dup(envelope[:outcome]), nil]
707
+ end
708
+
709
+ def run_template_directory_session_request(request)
710
+ normalized = deep_dup(request)
711
+ unless normalized[:ready] || normalized["ready"]
712
+ mode = normalized[:mode] || normalized["mode"]
713
+ diagnostics = normalized[:diagnostics] || normalized["diagnostics"] || []
714
+ return report_template_directory_session_configuration_outcome(
715
+ mode,
716
+ { mode: mode, ready: false, diagnostics: diagnostics }
717
+ )
718
+ end
719
+
720
+ run_template_directory_session_with_options(
721
+ normalized[:resolved_options] || normalized["resolved_options"]
722
+ )
723
+ end
724
+
725
+ def template_directory_session_request_envelope(request)
726
+ {
727
+ kind: "template_directory_session_request",
728
+ version: SESSION_REQUEST_TRANSPORT_VERSION,
729
+ request: deep_dup(request)
730
+ }
731
+ end
732
+
733
+ def import_template_directory_session_request_envelope(envelope)
734
+ return [nil, { category: "kind_mismatch", message: "expected template_directory_session_request envelope kind." }] unless envelope[:kind] == "template_directory_session_request"
735
+ return [nil, { category: "unsupported_version", message: "unsupported template_directory_session_request envelope version #{envelope[:version]}." }] unless envelope[:version] == SESSION_REQUEST_TRANSPORT_VERSION
736
+
737
+ [deep_dup(envelope[:request]), nil]
738
+ end
739
+
740
+ def run_template_directory_session_runner_request(request, profiles = {})
741
+ normalized = deep_dup(request)
742
+ request_kind = normalized[:request_kind] || normalized["request_kind"]
743
+ if request_kind.to_s == "profile"
744
+ return run_template_directory_session_request(
745
+ report_template_directory_session_profile_request(
746
+ profiles,
747
+ normalized[:profile_name] || normalized["profile_name"],
748
+ normalized[:overrides] || normalized["overrides"] || {}
749
+ )
750
+ )
751
+ end
752
+
753
+ run_template_directory_session_request(
754
+ report_template_directory_session_options_request(
755
+ normalized[:options] || normalized["options"] || {}
756
+ )
757
+ )
758
+ end
759
+
760
+ def template_directory_session_runner_request_envelope(request)
761
+ {
762
+ kind: "template_directory_session_runner_request",
763
+ version: SESSION_RUNNER_REQUEST_TRANSPORT_VERSION,
764
+ request: deep_dup(request)
765
+ }
766
+ end
767
+
768
+ def import_template_directory_session_runner_request_envelope(envelope)
769
+ return [nil, { category: "kind_mismatch", message: "expected template_directory_session_runner_request envelope kind." }] unless envelope[:kind] == "template_directory_session_runner_request"
770
+ return [nil, { category: "unsupported_version", message: "unsupported template_directory_session_runner_request envelope version #{envelope[:version]}." }] unless envelope[:version] == SESSION_RUNNER_REQUEST_TRANSPORT_VERSION
771
+
772
+ [deep_dup(envelope[:request]), nil]
773
+ end
774
+
775
+ def report_template_directory_session_runner_input(input)
776
+ normalized = deep_dup(input)
777
+ request_kind = (normalized[:request_kind] || normalized["request_kind"]).to_s
778
+ options = {
779
+ mode: normalized[:mode] || normalized["mode"],
780
+ template_root: normalized[:template_root] || normalized["template_root"],
781
+ destination_root: normalized[:destination_root] || normalized["destination_root"],
782
+ context: normalized[:context] || normalized["context"] || {},
783
+ default_strategy: normalized[:default_strategy] || normalized["default_strategy"] || "merge",
784
+ overrides: normalized[:overrides] || normalized["overrides"] || [],
785
+ replacements: normalized[:replacements] || normalized["replacements"] || {},
786
+ allowed_families: normalized.key?(:allowed_families) || normalized.key?("allowed_families") ?
787
+ (normalized[:allowed_families] || normalized["allowed_families"]) : nil
788
+ }
789
+ return {
790
+ request_kind: "profile",
791
+ profile_name: normalized[:profile_name] || normalized["profile_name"],
792
+ overrides: begin
793
+ sparse = {
794
+ mode: normalized[:mode] || normalized["mode"],
795
+ template_root: normalized[:template_root] || normalized["template_root"],
796
+ destination_root: normalized[:destination_root] || normalized["destination_root"]
797
+ }
798
+ context = normalized[:context] || normalized["context"]
799
+ sparse[:context] = context if context.is_a?(Hash) && (context[:project_name] || context["project_name"])
800
+ strategy = normalized[:default_strategy] || normalized["default_strategy"]
801
+ sparse[:default_strategy] = strategy if strategy && strategy != "merge"
802
+ overrides = normalized[:overrides] || normalized["overrides"]
803
+ sparse[:overrides] = overrides if overrides.is_a?(Array) && !overrides.empty?
804
+ replacements = normalized[:replacements] || normalized["replacements"]
805
+ sparse[:replacements] = replacements if replacements.is_a?(Hash) && !replacements.empty?
806
+ allowed_families = normalized.key?(:allowed_families) ? normalized[:allowed_families] : normalized["allowed_families"]
807
+ sparse[:allowed_families] = allowed_families unless allowed_families.nil?
808
+ sparse
809
+ end
810
+ } if request_kind == "profile"
811
+
812
+ {
813
+ request_kind: "options",
814
+ options: options
815
+ }
816
+ end
817
+
818
+ def report_template_directory_session_runner_payload(payload)
819
+ normalized = deep_dup(payload)
820
+ request_kind = normalized[:request_kind] || normalized["request_kind"]
821
+ request_kind = if request_kind.to_s.empty?
822
+ (normalized.key?(:profile_name) || normalized.key?("profile_name") ||
823
+ normalized.key?(:default_profile_name) || normalized.key?("default_profile_name")) ? "profile" : "options"
824
+ else
825
+ request_kind.to_s
826
+ end
827
+
828
+ result = {
829
+ request_kind: request_kind,
830
+ mode: normalized[:mode] || normalized["mode"],
831
+ template_root: normalized[:template_root] || normalized["template_root"],
832
+ destination_root: normalized[:destination_root] || normalized["destination_root"],
833
+ context: normalized[:context] || normalized["context"] || {},
834
+ default_strategy: normalized[:default_strategy] || normalized["default_strategy"] || "merge",
835
+ overrides: normalized[:overrides] || normalized["overrides"] || [],
836
+ replacements: normalized[:replacements] || normalized["replacements"] || {},
837
+ allowed_families: if normalized.key?(:allowed_families) || normalized.key?("allowed_families")
838
+ normalized.key?(:allowed_families) ? normalized[:allowed_families] : normalized["allowed_families"]
839
+ else
840
+ nil
841
+ end
842
+ }
843
+ profile_name = normalized[:profile_name] || normalized["profile_name"] ||
844
+ normalized[:default_profile_name] || normalized["default_profile_name"]
845
+ result[:profile_name] = profile_name if profile_name
846
+ result
847
+ end
848
+
849
+ def run_template_directory_session_runner_payload(payload, profiles = {})
850
+ run_template_directory_session_runner_request(
851
+ report_template_directory_session_runner_input(
852
+ report_template_directory_session_runner_payload(payload)
853
+ ),
854
+ profiles
855
+ )
856
+ end
857
+
858
+ def template_directory_session_runner_payload_envelope(payload)
859
+ {
860
+ kind: "template_directory_session_runner_payload",
861
+ version: SESSION_RUNNER_PAYLOAD_TRANSPORT_VERSION,
862
+ payload: deep_dup(payload)
863
+ }
864
+ end
865
+
866
+ def import_template_directory_session_runner_payload_envelope(envelope)
867
+ return [nil, { category: "kind_mismatch", message: "expected template_directory_session_runner_payload envelope kind." }] unless envelope[:kind] == "template_directory_session_runner_payload"
868
+ return [nil, { category: "unsupported_version", message: "unsupported template_directory_session_runner_payload envelope version #{envelope[:version]}." }] unless envelope[:version] == SESSION_RUNNER_PAYLOAD_TRANSPORT_VERSION
869
+
870
+ [deep_dup(envelope[:payload]), nil]
871
+ end
872
+
873
+ def run_template_directory_session_entrypoint(entrypoint, profiles = {})
874
+ normalized = deep_dup(entrypoint)
875
+ return run_template_directory_session_runner_payload(
876
+ normalized[:payload] || normalized["payload"],
877
+ profiles
878
+ ) if normalized[:payload] || normalized["payload"]
879
+
880
+ return run_template_directory_session_runner_request(
881
+ normalized[:request] || normalized["request"],
882
+ profiles
883
+ ) if normalized[:request] || normalized["request"]
884
+
885
+ report_template_directory_session_configuration_outcome(
886
+ "plan",
887
+ { mode: "plan", ready: false, diagnostics: [] }
888
+ )
889
+ end
890
+
891
+ def report_template_directory_session_entrypoint(entrypoint)
892
+ normalized = deep_dup(entrypoint)
893
+ if normalized[:payload] || normalized["payload"]
894
+ return {
895
+ source_kind: "payload",
896
+ runner_request: report_template_directory_session_runner_input(
897
+ report_template_directory_session_runner_payload(
898
+ normalized[:payload] || normalized["payload"]
899
+ )
900
+ )
901
+ }
902
+ end
903
+
904
+ if normalized[:request] || normalized["request"]
905
+ return {
906
+ source_kind: "request",
907
+ runner_request: normalized[:request] || normalized["request"]
908
+ }
909
+ end
910
+
911
+ {
912
+ source_kind: "",
913
+ runner_request: { request_kind: "options" }
914
+ }
915
+ end
916
+
917
+ def report_template_directory_session_resolution(entrypoint, profiles = {})
918
+ entrypoint_report = report_template_directory_session_entrypoint(entrypoint)
919
+ {
920
+ source_kind: entrypoint_report[:source_kind],
921
+ runner_request: entrypoint_report[:runner_request],
922
+ session_request: report_session_request_from_runner_request(
923
+ entrypoint_report[:runner_request],
924
+ profiles
925
+ )
926
+ }
927
+ end
928
+
929
+ def report_template_directory_session_inspection(entrypoint, profiles = {})
930
+ entrypoint_report = report_template_directory_session_entrypoint(entrypoint)
931
+ session_resolution = report_template_directory_session_resolution(entrypoint, profiles)
932
+ session_request = session_resolution[:session_request]
933
+
934
+ unless session_request[:ready] && session_request[:resolved_options]
935
+ return {
936
+ entrypoint_report: entrypoint_report,
937
+ session_resolution: session_resolution,
938
+ adapter_capabilities: {
939
+ required_families: [],
940
+ adapter_families: [],
941
+ missing_families: [],
942
+ ready: false
943
+ },
944
+ status: {
945
+ mode: session_request[:mode],
946
+ ready: false,
947
+ missing_families: [],
948
+ blocked_paths: [],
949
+ planned_write_count: 0,
950
+ written_count: 0
951
+ },
952
+ diagnostics: {
953
+ mode: session_request[:mode],
954
+ ready: false,
955
+ diagnostics: session_request[:diagnostics]
956
+ }
957
+ }
958
+ end
959
+
960
+ resolved = session_request[:resolved_options]
961
+ adapter_capabilities = report_default_adapter_capabilities_from_directories(
962
+ resolved[:template_root],
963
+ resolved[:destination_root],
964
+ resolved[:context],
965
+ resolved[:default_strategy],
966
+ resolved[:overrides],
967
+ resolved[:replacements],
968
+ resolved[:allowed_families],
969
+ resolved[:config]
970
+ )
971
+ session_report = plan_template_directory_session_from_directories(
972
+ resolved[:template_root],
973
+ resolved[:destination_root],
974
+ resolved[:context],
975
+ resolved[:default_strategy],
976
+ resolved[:overrides],
977
+ resolved[:replacements],
978
+ resolved[:config]
979
+ )
980
+
981
+ {
982
+ entrypoint_report: entrypoint_report,
983
+ session_resolution: session_resolution,
984
+ adapter_capabilities: adapter_capabilities,
985
+ status: report_template_directory_session_status(
986
+ report_template_directory_session_envelope(session_report, adapter_capabilities)
987
+ ),
988
+ diagnostics: plan_template_directory_session_diagnostics_from_directories(
989
+ resolved[:template_root],
990
+ resolved[:destination_root],
991
+ resolved[:context],
992
+ resolved[:default_strategy],
993
+ resolved[:overrides],
994
+ resolved[:replacements],
995
+ resolved[:allowed_families],
996
+ resolved[:config]
997
+ )
998
+ }
999
+ end
1000
+
1001
+ def template_directory_session_inspection_envelope(inspection)
1002
+ {
1003
+ kind: "template_directory_session_inspection",
1004
+ version: SESSION_INSPECTION_TRANSPORT_VERSION,
1005
+ inspection: deep_dup(inspection)
1006
+ }
1007
+ end
1008
+
1009
+ def import_template_directory_session_inspection_envelope(envelope)
1010
+ return [nil, { category: "kind_mismatch", message: "expected template_directory_session_inspection envelope kind." }] unless envelope[:kind] == "template_directory_session_inspection"
1011
+ return [nil, { category: "unsupported_version", message: "unsupported template_directory_session_inspection envelope version #{envelope[:version]}." }] unless envelope[:version] == SESSION_INSPECTION_TRANSPORT_VERSION
1012
+
1013
+ [deep_dup(envelope[:inspection]), nil]
1014
+ end
1015
+
1016
+ def run_template_directory_session_dispatch(operation, entrypoint, profiles = {})
1017
+ case operation.to_s
1018
+ when "inspect"
1019
+ {
1020
+ operation: operation.to_s,
1021
+ inspection: report_template_directory_session_inspection(entrypoint, profiles),
1022
+ outcome: nil
1023
+ }
1024
+ when "run"
1025
+ {
1026
+ operation: operation.to_s,
1027
+ inspection: nil,
1028
+ outcome: run_template_directory_session_entrypoint(entrypoint, profiles)
1029
+ }
1030
+ else
1031
+ raise ArgumentError, "unsupported template directory session operation: #{operation}"
1032
+ end
1033
+ end
1034
+
1035
+ def run_template_directory_session_command(command, profiles = {})
1036
+ normalized = deep_dup(command)
1037
+ run_template_directory_session_dispatch(
1038
+ normalized[:operation] || normalized["operation"],
1039
+ {
1040
+ payload: normalized[:payload] || normalized["payload"],
1041
+ request: normalized[:request] || normalized["request"]
1042
+ },
1043
+ profiles
1044
+ )
1045
+ end
1046
+
1047
+ def run_template_directory_session_command_payload(command, profiles = {})
1048
+ normalized = deep_dup(command)
1049
+ run_template_directory_session_command(
1050
+ {
1051
+ operation: normalized[:operation] || normalized["operation"],
1052
+ payload: {
1053
+ request_kind: normalized[:request_kind] || normalized["request_kind"],
1054
+ default_profile_name: normalized[:default_profile_name] || normalized["default_profile_name"],
1055
+ profile_name: normalized[:profile_name] || normalized["profile_name"],
1056
+ mode: normalized[:mode] || normalized["mode"],
1057
+ template_root: normalized[:template_root] || normalized["template_root"],
1058
+ destination_root: normalized[:destination_root] || normalized["destination_root"],
1059
+ context: normalized[:context] || normalized["context"],
1060
+ default_strategy: normalized[:default_strategy] || normalized["default_strategy"],
1061
+ overrides: normalized[:overrides] || normalized["overrides"],
1062
+ replacements: normalized[:replacements] || normalized["replacements"],
1063
+ allowed_families: normalized.key?(:allowed_families) ? normalized[:allowed_families] : normalized["allowed_families"]
1064
+ }
1065
+ },
1066
+ profiles
1067
+ )
1068
+ end
1069
+
1070
+ def run_template_directory_session(invocation, profiles = {})
1071
+ normalized = deep_dup(invocation)
1072
+ if normalized[:payload] || normalized["payload"] || normalized[:request] || normalized["request"]
1073
+ return run_template_directory_session_command(normalized, profiles)
1074
+ end
1075
+
1076
+ run_template_directory_session_command_payload(normalized, profiles)
1077
+ end
1078
+
1079
+ def template_directory_session_entrypoint_envelope(entrypoint)
1080
+ {
1081
+ kind: "template_directory_session_entrypoint",
1082
+ version: SESSION_ENTRYPOINT_TRANSPORT_VERSION,
1083
+ entrypoint: deep_dup(entrypoint)
1084
+ }
1085
+ end
1086
+
1087
+ def import_template_directory_session_entrypoint_envelope(envelope)
1088
+ return [nil, { category: "kind_mismatch", message: "expected template_directory_session_entrypoint envelope kind." }] unless envelope[:kind] == "template_directory_session_entrypoint"
1089
+ return [nil, { category: "unsupported_version", message: "unsupported template_directory_session_entrypoint envelope version #{envelope[:version]}." }] unless envelope[:version] == SESSION_ENTRYPOINT_TRANSPORT_VERSION
1090
+
1091
+ [deep_dup(envelope[:entrypoint]), nil]
1092
+ end
1093
+
1094
+ def template_directory_session_command_envelope(command)
1095
+ {
1096
+ kind: "template_directory_session_command",
1097
+ version: SESSION_COMMAND_TRANSPORT_VERSION,
1098
+ command: deep_dup(command)
1099
+ }
1100
+ end
1101
+
1102
+ def import_template_directory_session_command_envelope(envelope)
1103
+ return [nil, { category: "kind_mismatch", message: "expected template_directory_session_command envelope kind." }] unless envelope[:kind] == "template_directory_session_command"
1104
+ return [nil, { category: "unsupported_version", message: "unsupported template_directory_session_command envelope version #{envelope[:version]}." }] unless envelope[:version] == SESSION_COMMAND_TRANSPORT_VERSION
1105
+
1106
+ [deep_dup(envelope[:command]), nil]
1107
+ end
1108
+
1109
+ def template_directory_session_command_payload_envelope(payload)
1110
+ {
1111
+ kind: "template_directory_session_command_payload",
1112
+ version: SESSION_COMMAND_PAYLOAD_TRANSPORT_VERSION,
1113
+ payload: deep_dup(payload)
1114
+ }
1115
+ end
1116
+
1117
+ def import_template_directory_session_command_payload_envelope(envelope)
1118
+ return [nil, { category: "kind_mismatch", message: "expected template_directory_session_command_payload envelope kind." }] unless envelope[:kind] == "template_directory_session_command_payload"
1119
+ return [nil, { category: "unsupported_version", message: "unsupported template_directory_session_command_payload envelope version #{envelope[:version]}." }] unless envelope[:version] == SESSION_COMMAND_PAYLOAD_TRANSPORT_VERSION
1120
+
1121
+ [deep_dup(envelope[:payload]), nil]
1122
+ end
1123
+
1124
+ def template_directory_session_invocation_envelope(invocation)
1125
+ {
1126
+ kind: "template_directory_session_invocation",
1127
+ version: SESSION_INVOCATION_TRANSPORT_VERSION,
1128
+ invocation: deep_dup(invocation)
1129
+ }
1130
+ end
1131
+
1132
+ def import_template_directory_session_invocation_envelope(envelope)
1133
+ return [nil, { category: "kind_mismatch", message: "expected template_directory_session_invocation envelope kind." }] unless envelope[:kind] == "template_directory_session_invocation"
1134
+ return [nil, { category: "unsupported_version", message: "unsupported template_directory_session_invocation envelope version #{envelope[:version]}." }] unless envelope[:version] == SESSION_INVOCATION_TRANSPORT_VERSION
1135
+
1136
+ [deep_dup(envelope[:invocation]), nil]
1137
+ end
1138
+
1139
+ def report_session_request_from_runner_request(request, profiles = {})
1140
+ normalized = deep_dup(request)
1141
+ if (normalized[:request_kind] || normalized["request_kind"]).to_s == "profile"
1142
+ return report_template_directory_session_profile_request(
1143
+ profiles,
1144
+ normalized[:profile_name] || normalized["profile_name"],
1145
+ normalized[:overrides] || normalized["overrides"] || {}
1146
+ )
1147
+ end
1148
+
1149
+ report_template_directory_session_options_request(
1150
+ normalized[:options] || normalized["options"] || {}
1151
+ )
1152
+ end
1153
+
1154
+ def resolve_template_directory_session_options(profiles, profile_name, overrides)
1155
+ profile = profiles[profile_name.to_s] || profiles[profile_name.to_sym]
1156
+ return nil unless profile
1157
+
1158
+ normalized_profile = deep_dup(profile)
1159
+ normalized_overrides = deep_dup(overrides)
1160
+ {
1161
+ mode: normalized_overrides[:mode] || normalized_overrides["mode"] ||
1162
+ normalized_profile[:mode] || normalized_profile["mode"],
1163
+ template_root: normalized_overrides[:template_root] || normalized_overrides["template_root"],
1164
+ destination_root: normalized_overrides[:destination_root] || normalized_overrides["destination_root"],
1165
+ context: normalized_overrides[:context] || normalized_overrides["context"] ||
1166
+ normalized_profile[:context] || normalized_profile["context"] || {},
1167
+ default_strategy: normalized_overrides[:default_strategy] || normalized_overrides["default_strategy"] ||
1168
+ normalized_profile[:default_strategy] || normalized_profile["default_strategy"],
1169
+ overrides: normalized_overrides[:overrides] || normalized_overrides["overrides"] ||
1170
+ normalized_profile[:overrides] || normalized_profile["overrides"] || [],
1171
+ replacements: normalized_overrides[:replacements] || normalized_overrides["replacements"] ||
1172
+ normalized_profile[:replacements] || normalized_profile["replacements"] || {},
1173
+ allowed_families: normalized_overrides[:allowed_families] || normalized_overrides["allowed_families"] ||
1174
+ normalized_profile[:allowed_families] || normalized_profile["allowed_families"],
1175
+ config: normalized_overrides[:config] || normalized_overrides["config"] ||
1176
+ normalized_profile[:config] || normalized_profile["config"]
1177
+ }
1178
+ end
1179
+
1180
+ def run_template_directory_session_with_profile(profiles, profile_name, overrides)
1181
+ request = report_template_directory_session_profile_request(profiles, profile_name, overrides)
1182
+ unless request[:ready]
1183
+ return report_template_directory_session_configuration_outcome(
1184
+ request[:mode],
1185
+ { mode: request[:mode], ready: request[:ready], diagnostics: request[:diagnostics] }
1186
+ )
1187
+ end
1188
+
1189
+ run_template_directory_session_with_options(request[:resolved_options])
1190
+ end
1191
+
1192
+ private
1193
+
1194
+ def deep_dup(value)
1195
+ Ast::Merge.deep_dup(value)
1196
+ end
1197
+
1198
+ def normalize_session_mode(mode)
1199
+ normalized_mode = mode.to_s
1200
+ MODES.include?(normalized_mode) ? normalized_mode : "plan"
1201
+ end
1202
+
1203
+ def compact_session_request_options(options)
1204
+ normalized = deep_dup(options)
1205
+ normalized.delete(:config)
1206
+ normalized.delete("config")
1207
+ normalized
1208
+ end
1209
+ end
1210
+ end
1211
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ast/template"
data.tar.gz.sig ADDED
@@ -0,0 +1,2 @@
1
+ fѵɤ����=]�H�qb�����iN�_4�^�W<���^Xs-5;�5e3��"�s ��-S@��¶��s�W�?v��Ð��
2
+ �l,}��:~���iP��%��sϞĨ���*�(��wT���(4�4kj{�bk%��z�E<=?��ašt���i�!.�$b����"��E�9�{��
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ast-template
3
+ version: !ruby/object:Gem::Version
4
+ version: 7.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Peter H. Boling
8
+ bindir: bin
9
+ cert_chain:
10
+ - |
11
+ -----BEGIN CERTIFICATE-----
12
+ MIIEgDCCAuigAwIBAgIBATANBgkqhkiG9w0BAQsFADBDMRUwEwYDVQQDDAxwZXRl
13
+ ci5ib2xpbmcxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkW
14
+ A2NvbTAeFw0yNTA1MDQxNTMzMDlaFw00NTA0MjkxNTMzMDlaMEMxFTATBgNVBAMM
15
+ DHBldGVyLmJvbGluZzEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPy
16
+ LGQBGRYDY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAruUoo0WA
17
+ uoNuq6puKWYeRYiZekz/nsDeK5x/0IEirzcCEvaHr3Bmz7rjo1I6On3gGKmiZs61
18
+ LRmQ3oxy77ydmkGTXBjruJB+pQEn7UfLSgQ0xa1/X3kdBZt6RmabFlBxnHkoaGY5
19
+ mZuZ5+Z7walmv6sFD9ajhzj+oIgwWfnEHkXYTR8I6VLN7MRRKGMPoZ/yvOmxb2DN
20
+ coEEHWKO9CvgYpW7asIihl/9GMpKiRkcYPm9dGQzZc6uTwom1COfW0+ZOFrDVBuV
21
+ FMQRPswZcY4Wlq0uEBLPU7hxnCL9nKK6Y9IhdDcz1mY6HZ91WImNslOSI0S8hRpj
22
+ yGOWxQIhBT3fqCBlRIqFQBudrnD9jSNpSGsFvbEijd5ns7Z9ZMehXkXDycpGAUj1
23
+ to/5cuTWWw1JqUWrKJYoifnVhtE1o1DZ+LkPtWxHtz5kjDG/zR3MG0Ula0UOavlD
24
+ qbnbcXPBnwXtTFeZ3C+yrWpE4pGnl3yGkZj9SMTlo9qnTMiPmuWKQDatAgMBAAGj
25
+ fzB9MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBQE8uWvNbPVNRXZ
26
+ HlgPbc2PCzC4bjAhBgNVHREEGjAYgRZwZXRlci5ib2xpbmdAZ21haWwuY29tMCEG
27
+ A1UdEgQaMBiBFnBldGVyLmJvbGluZ0BnbWFpbC5jb20wDQYJKoZIhvcNAQELBQAD
28
+ ggGBAJbnUwfJQFPkBgH9cL7hoBfRtmWiCvdqdjeTmi04u8zVNCUox0A4gT982DE9
29
+ wmuN12LpdajxZONqbXuzZvc+nb0StFwmFYZG6iDwaf4BPywm2e/Vmq0YG45vZXGR
30
+ L8yMDSK1cQXjmA+ZBKOHKWavxP6Vp7lWvjAhz8RFwqF9GuNIdhv9NpnCAWcMZtpm
31
+ GUPyIWw/Cw/2wZp74QzZj6Npx+LdXoLTF1HMSJXZ7/pkxLCsB8m4EFVdb/IrW/0k
32
+ kNSfjtAfBHO8nLGuqQZVH9IBD1i9K6aSs7pT6TW8itXUIlkIUI2tg5YzW6OFfPzq
33
+ QekSkX3lZfY+HTSp/o+YvKkqWLUV7PQ7xh1ZYDtocpaHwgxe/j3bBqHE+CUPH2vA
34
+ 0V/FwdTRWcwsjVoOJTrYcff8pBZ8r2MvtAc54xfnnhGFzeRHfcltobgFxkAXdE6p
35
+ DVjBtqT23eugOqQ73umLcYDZkc36vnqGxUBSsXrzY9pzV5gGr2I8YUxMqf6ATrZt
36
+ L9nRqA==
37
+ -----END CERTIFICATE-----
38
+ date: 1980-01-02 00:00:00.000000000 Z
39
+ dependencies:
40
+ - !ruby/object:Gem::Dependency
41
+ name: ast-merge
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - '='
45
+ - !ruby/object:Gem::Version
46
+ version: 7.0.0
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - '='
52
+ - !ruby/object:Gem::Version
53
+ version: 7.0.0
54
+ description: Product-level directory session reporting above ast-merge for Structured
55
+ Merge.
56
+ email:
57
+ - info@structuredmerge.org
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - lib/ast-template.rb
63
+ - lib/ast/template.rb
64
+ - lib/ast/template/version.rb
65
+ homepage: https://github.com/structuredmerge/structuredmerge-ruby
66
+ licenses:
67
+ - AGPL-3.0-only
68
+ - PolyForm-Small-Business-1.0.0
69
+ metadata:
70
+ homepage_uri: https://structuredmerge.org
71
+ source_code_uri: https://github.com/structuredmerge/structuredmerge-ruby/tree/v7.0.0
72
+ changelog_uri: https://github.com/structuredmerge/structuredmerge-ruby/blob/v7.0.0/CHANGELOG.md
73
+ bug_tracker_uri: https://github.com/structuredmerge/structuredmerge-ruby/issues
74
+ documentation_uri: https://www.rubydoc.info/gems/ast-template/7.0.0
75
+ funding_uri: https://github.com/sponsors/pboling
76
+ wiki_uri: https://github.com/structuredmerge/structuredmerge-ruby/wiki
77
+ discord_uri: https://discord.gg/3qme4XHNKN
78
+ rubygems_mfa_required: 'true'
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: 4.0.0
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubygems_version: 4.0.10
94
+ specification_version: 4
95
+ summary: Structured Merge template orchestration contracts for the Ruby monorepo
96
+ test_files: []
metadata.gz.sig ADDED
Binary file