orfeas_lyra 0.6.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 +7 -0
- data/CHANGELOG.md +222 -0
- data/LICENSE +21 -0
- data/README.md +1165 -0
- data/Rakefile +728 -0
- data/app/controllers/lyra/application_controller.rb +23 -0
- data/app/controllers/lyra/dashboard_controller.rb +624 -0
- data/app/controllers/lyra/flow_controller.rb +224 -0
- data/app/controllers/lyra/privacy_controller.rb +182 -0
- data/app/views/lyra/dashboard/audit_trail.html.erb +324 -0
- data/app/views/lyra/dashboard/discrepancies.html.erb +125 -0
- data/app/views/lyra/dashboard/event_graph_view.html.erb +525 -0
- data/app/views/lyra/dashboard/heatmap_view.html.erb +155 -0
- data/app/views/lyra/dashboard/index.html.erb +119 -0
- data/app/views/lyra/dashboard/model_overview.html.erb +115 -0
- data/app/views/lyra/dashboard/projections.html.erb +302 -0
- data/app/views/lyra/dashboard/schema.html.erb +283 -0
- data/app/views/lyra/dashboard/schema_history.html.erb +78 -0
- data/app/views/lyra/dashboard/schema_version.html.erb +340 -0
- data/app/views/lyra/dashboard/verification.html.erb +370 -0
- data/app/views/lyra/flow/crud_mapping.html.erb +125 -0
- data/app/views/lyra/flow/timeline.html.erb +260 -0
- data/app/views/lyra/privacy/pii_detection.html.erb +148 -0
- data/app/views/lyra/privacy/policy.html.erb +188 -0
- data/app/workflows/es_async_mode_workflow.rb +80 -0
- data/app/workflows/es_sync_mode_workflow.rb +64 -0
- data/app/workflows/hijack_mode_workflow.rb +54 -0
- data/app/workflows/lifecycle_workflow.rb +43 -0
- data/app/workflows/monitor_mode_workflow.rb +39 -0
- data/config/privacy_policies.rb +273 -0
- data/config/routes.rb +48 -0
- data/lib/lyra/aggregate.rb +131 -0
- data/lib/lyra/associations/event_aware.rb +225 -0
- data/lib/lyra/command.rb +81 -0
- data/lib/lyra/command_handler.rb +155 -0
- data/lib/lyra/configuration.rb +124 -0
- data/lib/lyra/consistency/read_your_writes.rb +91 -0
- data/lib/lyra/correlation.rb +144 -0
- data/lib/lyra/dual_view.rb +231 -0
- data/lib/lyra/engine.rb +67 -0
- data/lib/lyra/event.rb +71 -0
- data/lib/lyra/event_analyzer.rb +135 -0
- data/lib/lyra/event_flow.rb +449 -0
- data/lib/lyra/event_mapper.rb +106 -0
- data/lib/lyra/event_store_adapter.rb +72 -0
- data/lib/lyra/id_generator.rb +137 -0
- data/lib/lyra/interceptors/association_interceptor.rb +169 -0
- data/lib/lyra/interceptors/crud_interceptor.rb +543 -0
- data/lib/lyra/privacy/gdpr_compliance.rb +161 -0
- data/lib/lyra/privacy/pii_detector.rb +85 -0
- data/lib/lyra/privacy/pii_masker.rb +66 -0
- data/lib/lyra/privacy/policy_integration.rb +253 -0
- data/lib/lyra/projection.rb +94 -0
- data/lib/lyra/projections/async_projection_job.rb +63 -0
- data/lib/lyra/projections/cached_projection.rb +322 -0
- data/lib/lyra/projections/cached_relation.rb +757 -0
- data/lib/lyra/projections/event_store_reader.rb +127 -0
- data/lib/lyra/projections/model_projection.rb +143 -0
- data/lib/lyra/schema/diff.rb +331 -0
- data/lib/lyra/schema/event_class_registrar.rb +63 -0
- data/lib/lyra/schema/generator.rb +190 -0
- data/lib/lyra/schema/reporter.rb +188 -0
- data/lib/lyra/schema/store.rb +156 -0
- data/lib/lyra/schema/validator.rb +100 -0
- data/lib/lyra/strict_data_access.rb +363 -0
- data/lib/lyra/verification/crud_lifecycle_workflow.rb +456 -0
- data/lib/lyra/verification/workflow_generator.rb +540 -0
- data/lib/lyra/version.rb +3 -0
- data/lib/lyra/visualization/activity_heatmap.rb +215 -0
- data/lib/lyra/visualization/event_graph.rb +310 -0
- data/lib/lyra/visualization/timeline.rb +398 -0
- data/lib/lyra.rb +150 -0
- data/lib/tasks/dist.rake +391 -0
- data/lib/tasks/gems.rake +185 -0
- data/lib/tasks/lyra_schema.rake +231 -0
- data/lib/tasks/lyra_workflows.rake +452 -0
- data/lib/tasks/public_release.rake +351 -0
- data/lib/tasks/stats.rake +175 -0
- data/lib/tasks/testbed.rake +479 -0
- data/lib/tasks/version.rake +159 -0
- metadata +221 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 3d1fc3fab85a30b1ed635864d084fee35468b77ff0ad0ce2aec0b0d004fe43e7
|
|
4
|
+
data.tar.gz: e050efff730bc551ef67c3d0107dc5c11fb5c5e8ba433970f188c17d3517e3c5
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 5650de44e8ade792aa79bf9fba6e42dd6144e08521a47aa5132e34df94215af9c464faa3d08f87c7d8b98a03649b0f4600e8636a3e334da89100eb0e257a6170
|
|
7
|
+
data.tar.gz: 03d0c2c5419ffddd950f86d8d97dcc3033be8bbd81dda0a8470ef9ab2c4e743c98484788cb74a59a41cf3933c946496f06201d77341b2c9280e58accd26012f7
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the Lyra project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.6.0] - 2026-01-05
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
#### Core CRUD-to-Event Sourcing Engine
|
|
15
|
+
- Monitor mode for non-intrusive CRUD operation logging
|
|
16
|
+
- Hijack mode for full event sourcing transformation
|
|
17
|
+
- Rails Event Store integration
|
|
18
|
+
- CRUD interceptor using ActiveRecord callbacks
|
|
19
|
+
- Event mapping layer (CRUD operations → Domain events)
|
|
20
|
+
- Command/Aggregate pattern for hijack mode
|
|
21
|
+
- Dual-view comparison (CRUD state vs Event-sourced state)
|
|
22
|
+
- State reconstruction from event streams
|
|
23
|
+
- Audit trail generation
|
|
24
|
+
- Dashboard API endpoints
|
|
25
|
+
- ERB-based dashboard views
|
|
26
|
+
- Pluggable event store backend
|
|
27
|
+
- Custom aggregate support
|
|
28
|
+
- Custom event mapper support
|
|
29
|
+
- Projection system for read models
|
|
30
|
+
- **Custom Metadata Proc** (`metadata_proc`) - Inject custom metadata into every event:
|
|
31
|
+
- Capture user context from `Current`/`CurrentAttributes`
|
|
32
|
+
- Support for Devise/Warden authentication
|
|
33
|
+
- Thread-safe per-request metadata
|
|
34
|
+
- Graceful error handling (returns `{}` on failure)
|
|
35
|
+
- **Rails Engine Namespaced Model Support** - Properly handle namespaced models:
|
|
36
|
+
- `MyEngine::Order` → `MyEngineOrderCreated` (sanitized event class name)
|
|
37
|
+
- Preserves original namespace in `model_class` attribute
|
|
38
|
+
- Supports deeply nested namespaces (e.g., `MyEngine::Admin::Widget`)
|
|
39
|
+
|
|
40
|
+
#### Privacy Extraction to PAM DSL
|
|
41
|
+
- **PAM DSL as Optional Dependency** - Privacy features gracefully degrade without PAM DSL
|
|
42
|
+
- **PIIDetector Delegation** - `Lyra::Privacy::PIIDetector` delegates to `PamDsl::PIIDetector`
|
|
43
|
+
- **PIIMasker Delegation** - `Lyra::Privacy::PIIMasker` delegates to `PamDsl::PIIMasker`
|
|
44
|
+
- **GDPRCompliance Delegation** - `Lyra::Privacy::GDPRCompliance` delegates to `PamDsl::GDPRCompliance`
|
|
45
|
+
- **PolicyIntegration Enhancements**:
|
|
46
|
+
- `use_detector: true` option (default) - Falls back to PIIDetector for fields not in policy
|
|
47
|
+
- `use_detector: false` - Policy-only mode, no pattern-based detection
|
|
48
|
+
- `pam_dsl_available?` - Check if PAM DSL is loaded
|
|
49
|
+
- `source: :policy` or `source: :detector` in detection results
|
|
50
|
+
- Retention returns `nil` (infinite/manual) when no policy defined
|
|
51
|
+
- 28 new tests for PolicyIntegration
|
|
52
|
+
|
|
53
|
+
#### Unified PII Detection System
|
|
54
|
+
- **Consolidated PIIDetector** - Lyra now delegates to `PamDsl::PIIDetector` for unified PII detection
|
|
55
|
+
- **Partial Matching Mode** (default) - Detects PII in compound field names using word boundaries
|
|
56
|
+
- Matches prefixed fields: `customer_email`, `billing_phone`, `user_name`
|
|
57
|
+
- Matches suffixed fields: `home_phone`, `work_email`, `shipping_address`
|
|
58
|
+
- Supports camelCase suffix boundaries: `emailAddress`, `phoneNumber`
|
|
59
|
+
- **Exact Matching Mode** - Optional strict mode for specific known field names only
|
|
60
|
+
- **Configurable via `PamDsl::PIIDetector.partial_match`** - Toggle between matching modes
|
|
61
|
+
- **New PII Types**:
|
|
62
|
+
- `:identifier` - VAT numbers, tax IDs, passport numbers, AFM (Greek tax ID)
|
|
63
|
+
- `:financial` - IBAN, bank accounts, salary, income
|
|
64
|
+
- `:health` - Medical records, diagnosis, prescriptions
|
|
65
|
+
- `:biometric` - Fingerprints, face ID, retina scans
|
|
66
|
+
- `:location` - GPS coordinates, latitude/longitude
|
|
67
|
+
- **Exclusion Patterns** - Prevents false positives on:
|
|
68
|
+
- Timestamps: `*_at`, `*_on`, `*_date`, `*_time`
|
|
69
|
+
- Counters/amounts: `*_count`, `*_amount`, `*_total`
|
|
70
|
+
- Flags: `*_verified`, `*_confirmed`, `*_enabled`, `is_*`, `has_*`
|
|
71
|
+
- Codes: `*_code` (except `postal_code`, `zip_code`)
|
|
72
|
+
- Security: `*_digest`, `*_token`, `*_hash`, `encrypted_*`
|
|
73
|
+
- **Sensitivity Levels with Legislative Background**:
|
|
74
|
+
- `:public` - Publicly accessible data
|
|
75
|
+
- `:internal` - Low risk, basic protection (GDPR Art. 6)
|
|
76
|
+
- `:confidential` - Medium risk, enhanced protection (GDPR Art. 6, Art. 32)
|
|
77
|
+
- `:restricted` - High risk, special categories (GDPR Art. 9, PCI DSS)
|
|
78
|
+
- **PII Masking** - Type-specific masking for safe display
|
|
79
|
+
- **56 tests with 188 assertions** for comprehensive coverage
|
|
80
|
+
|
|
81
|
+
#### Event Schema Validation System
|
|
82
|
+
- **Schema Store** (`lib/lyra/schema/store.rb`) - Versioned schema file persistence in `db/lyra_schemas/`
|
|
83
|
+
- **Schema Generator** (`lib/lyra/schema/generator.rb`) - Generates schemas from monitored ActiveRecord models
|
|
84
|
+
- **Schema Diff** (`lib/lyra/schema/diff.rb`) - Compares schemas with severity classification (BREAKING, WARNING, INFO)
|
|
85
|
+
- **Schema Validator** (`lib/lyra/schema/validator.rb`) - Enforces schema consistency with strict mode support
|
|
86
|
+
- **Schema Reporter** (`lib/lyra/schema/reporter.rb`) - Human-readable model→event mapping reports
|
|
87
|
+
- **Rake Tasks** for schema management:
|
|
88
|
+
- `rake lyra:schema:create` - Generate initial schema
|
|
89
|
+
- `rake lyra:schema:update` - Create new schema version
|
|
90
|
+
- `rake lyra:schema:verify` - Check for schema changes
|
|
91
|
+
- `rake lyra:schema:report` - Display model→event mappings
|
|
92
|
+
- `rake lyra:schema:history` - Show version history
|
|
93
|
+
- `rake lyra:schema:diff[v1,v2]` - Compare two schema versions
|
|
94
|
+
|
|
95
|
+
#### Event Class Registration for RailsEventStore Compatibility
|
|
96
|
+
- **EventClassRegistrar** (`lib/lyra/schema/event_class_registrar.rb`) - Pre-registers event classes at Rails startup
|
|
97
|
+
- Ensures `Object.const_get()` works when RailsEventStore deserializes events after app restart
|
|
98
|
+
- Registers events from both monitored models configuration and stored schema (for legacy events)
|
|
99
|
+
|
|
100
|
+
#### Event Sourcing with Disabled Projections (Sixth Mode)
|
|
101
|
+
- **CachedRelation** (`lib/lyra/projections/cached_relation.rb`) - ActiveRecord::Relation-like wrapper for in-memory cached data
|
|
102
|
+
- Full query interface: `where`, `order`, `limit`, `offset`, `first`, `last`, `find`, `find_by`
|
|
103
|
+
- Type coercion for string/integer and boolean comparisons (handles params from controllers)
|
|
104
|
+
- Aggregation methods: `sum`, `average`, `minimum`, `maximum`, `pluck`
|
|
105
|
+
- Pagination support: `page`, `per` (Kaminari-compatible)
|
|
106
|
+
- Chainable no-ops for AR methods: `includes`, `joins`, `preload`, `distinct`
|
|
107
|
+
- Rails 8 Arel predicate value extraction support
|
|
108
|
+
|
|
109
|
+
- **AssociationInterceptor** (`lib/lyra/interceptors/association_interceptor.rb`) - Patches AR associations for cache-based loading
|
|
110
|
+
- `BelongsToAssociationPatch` - Intercepts belongs_to association loading
|
|
111
|
+
- `HasOneAssociationPatch` - Intercepts has_one association loading
|
|
112
|
+
- `HasManyAssociationPatch` - Intercepts has_many association loading with count/size support
|
|
113
|
+
- Polymorphic association support for belongs_to
|
|
114
|
+
- Rails 8 async: keyword argument compatibility
|
|
115
|
+
|
|
116
|
+
- **EventStoreReader** (`lib/lyra/projections/event_store_reader.rb`) - Reads and caches entity state from event store
|
|
117
|
+
- Reconstructs current state by replaying events
|
|
118
|
+
- Solid Cache / Rails.cache integration for fast reads
|
|
119
|
+
- Cache warming and invalidation on writes
|
|
120
|
+
- Full query interface via CachedRelation
|
|
121
|
+
|
|
122
|
+
- **CrudInterceptor first/last overrides** - Proper ordering by primary key
|
|
123
|
+
- `Model.first` now orders by `primary_key ASC` before returning first record
|
|
124
|
+
- `Model.last` now orders by `primary_key DESC` before returning first record
|
|
125
|
+
|
|
126
|
+
#### Benchmark Infrastructure
|
|
127
|
+
- **Mode Comparison Benchmark** (`perf/run_mode_comparison_benchmark.rb`)
|
|
128
|
+
- Comprehensive benchmarking across all 7 Lyra modes
|
|
129
|
+
- Scales from 500 to 100,000 operations
|
|
130
|
+
- Four scenarios: CRUD, batch, query, mixed workloads
|
|
131
|
+
- Checkpoint/resume support for long-running benchmarks
|
|
132
|
+
- Automatic chart generation (PNG) via gnuplot
|
|
133
|
+
- JSON, CSV, and Markdown report output
|
|
134
|
+
- **Object allocations tracking** - measures Ruby allocations per operation via GC.stat
|
|
135
|
+
- **Allocations charts** - per-scale bar charts and scaling comparison line chart
|
|
136
|
+
- Warmup phase for JIT/cache priming
|
|
137
|
+
- Cache warming for ES Disabled mode before measurements
|
|
138
|
+
|
|
139
|
+
#### Testing Infrastructure
|
|
140
|
+
- **Comprehensive CachedRelation test suite** (`test/projections/cached_relation_test.rb`)
|
|
141
|
+
- 37 tests covering enumerable, finders, where filtering, type coercion, ordering, pagination, aggregations
|
|
142
|
+
- Isolated unit tests with MockRecord and MockModel classes
|
|
143
|
+
|
|
144
|
+
- **Unified 6-mode test runner** - All Lyra modes tested together
|
|
145
|
+
- `rake lyra:test:all_modes` runs all 6 configurations in single execution
|
|
146
|
+
- Configurations: disabled, monitor, hijack, event_sourcing+sync, event_sourcing+async, event_sourcing+disabled
|
|
147
|
+
- Comprehensive report with pass/fail status per configuration
|
|
148
|
+
|
|
149
|
+
#### Configuration Options
|
|
150
|
+
- `strict_schema` - Fail startup if schema changes detected (recommended for production)
|
|
151
|
+
- `schema_path` - Custom path for schema files (defaults to `db/lyra_schemas/`)
|
|
152
|
+
|
|
153
|
+
#### Database Support
|
|
154
|
+
- PostgreSQL primary database support
|
|
155
|
+
- SQLite alternative for small deployments
|
|
156
|
+
|
|
157
|
+
#### Example Application
|
|
158
|
+
- Aegean E-Pay Testbed with student registrations
|
|
159
|
+
- Payment processing with multiple channels
|
|
160
|
+
- Complete database schema and seed data
|
|
161
|
+
|
|
162
|
+
### Fixed
|
|
163
|
+
|
|
164
|
+
#### StrictDataAccess Compatibility with Event Sourcing Mode
|
|
165
|
+
- Fixed conflict between `StrictDataAccess` and ES mode sync projections
|
|
166
|
+
- Projections now correctly bypass strict access checks (using `Thread.current[:lyra_bypass_strict_access]`)
|
|
167
|
+
- This is safe because projections are internal Lyra operations with events already recorded
|
|
168
|
+
- Fixed require order in `lib/lyra.rb` to load `strict_data_access` before projections
|
|
169
|
+
|
|
170
|
+
#### CachedRelation Exception Handling in ES Disabled Mode
|
|
171
|
+
- Fixed `CachedRelation.method_missing` swallowing `StrictDataAccessViolation` exceptions
|
|
172
|
+
- `update_all` and `delete_all` on `CachedRelation` now correctly raise violations when strict mode is enabled
|
|
173
|
+
- The generic scope delegation rescue clause now re-raises framework violations instead of suppressing them
|
|
174
|
+
|
|
175
|
+
#### Automatic Event Creation for `dependent: :nullify` Operations
|
|
176
|
+
- When `dependent: :nullify` triggers `update_all(foreign_key: nil)`, Lyra now creates events for affected child records
|
|
177
|
+
- This ensures the event stream captures foreign key nullification when parent records are destroyed
|
|
178
|
+
- Events include `nullify_source: "dependent_association"` in metadata for traceability
|
|
179
|
+
- Works in both DB-backed modes (plucks from DB) and ES Disabled mode (queries event store cache)
|
|
180
|
+
- Resolves event stream completeness gap where child state changes were previously invisible
|
|
181
|
+
|
|
182
|
+
#### Automatic Event Creation for Callback-Bypassing Operations
|
|
183
|
+
- `update_columns` and `update_column` now create events when allowed (strict mode off or bypassed)
|
|
184
|
+
- `delete` now creates a "Destroyed" event when allowed
|
|
185
|
+
- Events include `bypass_source: "update_columns"` or `bypass_source: "delete"` in metadata
|
|
186
|
+
- This ensures the event stream remains complete even when developers intentionally bypass callbacks
|
|
187
|
+
- Only applies to Lyra-monitored models when mode is not `:disabled`
|
|
188
|
+
|
|
189
|
+
#### Test Isolation Issues
|
|
190
|
+
- Fixed test contamination between controller and integration tests
|
|
191
|
+
- Integration tests now create their own event store if none exists
|
|
192
|
+
- Added `create_event_store` helper to `multi_mode_integration_test.rb` and `event_sourcing_mode_test.rb`
|
|
193
|
+
- Fixed `assert_not_nil` → `refute_nil` (Minitest syntax)
|
|
194
|
+
- Added proper `Lyra.config.monitor_model` calls for correct event naming
|
|
195
|
+
|
|
196
|
+
### Changed
|
|
197
|
+
|
|
198
|
+
#### Test Coverage Configuration
|
|
199
|
+
- Increased SimpleCov minimum coverage thresholds (50% line, 47% branch)
|
|
200
|
+
- Added coverage groups for Projections and Schema modules
|
|
201
|
+
- All 584 tests now pass with proper isolation
|
|
202
|
+
- Added comprehensive tests for Schema::Diff (column limit changes, event renames, config changes)
|
|
203
|
+
- Added comprehensive tests for Schema::Reporter (report generation, PII summaries, model mappings)
|
|
204
|
+
- Added comprehensive tests for Visualization::Timeline (Mermaid, ASCII, D3.js output)
|
|
205
|
+
- Added comprehensive tests for EventAnalyzer (timeline, operations, privacy analysis)
|
|
206
|
+
- Added comprehensive tests for CommandHandler (event data, metadata, error handling)
|
|
207
|
+
- Added comprehensive tests for DualView (value normalization, type coercion)
|
|
208
|
+
|
|
209
|
+
### Core Components
|
|
210
|
+
- `CrudInterceptor` - ActiveRecord callback integration
|
|
211
|
+
- `EventMapper` - CRUD to event mapping
|
|
212
|
+
- `Command` - Command pattern implementation
|
|
213
|
+
- `CommandHandler` - Command processing
|
|
214
|
+
- `Aggregate` - Domain aggregate base class
|
|
215
|
+
- `GenericAggregate` - Default aggregate for monitored models
|
|
216
|
+
- `Projection` - Read model projection base
|
|
217
|
+
- `StateProjection` - State reconstruction
|
|
218
|
+
- `AuditProjection` - Audit trail generation
|
|
219
|
+
- `DualView` - CRUD vs Event-sourced comparison
|
|
220
|
+
- `StateAnalyzer` - State analysis and recommendations
|
|
221
|
+
- `EventStoreAdapter` - Pluggable event storage
|
|
222
|
+
- `Configuration` - Centralized configuration
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019-2026 Michail Pantelelis
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|