From 29cea479aab73bc154764eeb7f661d615d59dbc2 Mon Sep 17 00:00:00 2001
From: Mathew Polzin <matt.polzin@gmail.com>
Date: Mon, 18 Mar 2024 20:04:25 -0500
Subject: [PATCH 1/5] experimenting

---
 Sources/OpenAPIKit/JSONDynamicReference.swift | 126 ++++++++++++++++++
 Sources/OpenAPIKit/JSONReference.swift        |   5 +
 2 files changed, 131 insertions(+)
 create mode 100644 Sources/OpenAPIKit/JSONDynamicReference.swift

diff --git a/Sources/OpenAPIKit/JSONDynamicReference.swift b/Sources/OpenAPIKit/JSONDynamicReference.swift
new file mode 100644
index 000000000..b012a0387
--- /dev/null
+++ b/Sources/OpenAPIKit/JSONDynamicReference.swift
@@ -0,0 +1,126 @@
+//
+//  JSONDynamicReference.swift
+//  
+//
+//  Created by Mathew Polzin.
+//
+
+import OpenAPIKitCore
+
+@dynamicMemberLookup
+public struct JSONDynamicReference: Equatable, Hashable {
+    public let jsonReference: JSONReference<JSONSchema>
+
+    public init(
+        _ reference: JSONReference<JSONSchema>
+    ) {
+        self.jsonReference = reference
+    }
+
+    public subscript<T>(dynamicMember path: KeyPath<JSONReference<JSONSchema>, T>) -> T {
+      return jsonReference[keyPath: path]
+    }
+
+    /// Reference a component of type `ReferenceType` in the
+    /// Components Object.
+    ///
+    /// Example:
+    ///
+    ///     JSONDynamicReference.component(named: "greetings")
+    ///     // encoded string: "#/components/schemas/greetings"
+    ///     // Swift: `document.components.schemas["greetings"]`
+    public static func component(named name: String) -> Self {
+      return .init(.internal(.component(name: name)), summary: summary, description: description)
+    }
+
+    /// Reference a path internal to this file but not within the Components Object
+    /// This is likely not what you are looking for. It is advisable to store reusable components
+    /// in the Components Object.
+    ///
+    /// - Important: The path does not contain a leading '#'. Start with the root '/'.
+    public static func `internal`(path: JSONReference<JSONSchema>.Path, summary: String? = nil, description: String? = nil) -> Self {
+      return .init(.internal(.path(path)), summary: summary, description: description)
+    }
+
+    /// Reference an external URL.
+    public static func external(_ url: URL) -> Self {
+      return .init(.external(url))
+    }
+
+    /// `true` for internal references, `false` for
+    /// external references (i.e. to another file).
+    public var isInternal: Bool {
+      return jsonReference.isInternal
+    }
+
+    /// `true` for external references, `false` for
+    /// internal references.
+    public var isExternal: Bool {
+      return jsonReference.isExternal
+    }
+
+    /// Get the name of the referenced object. This method returns optional
+    /// because a reference to an external file might not have any path if the
+    /// file itself is the referenced component.
+    public var name: String? {
+      return jsonReference.name
+    }
+
+    /// The absolute value of an external reference's
+    /// URL or the path fragment string for a local
+    /// reference as defined in [RFC 3986](https://tools.ietf.org/html/rfc3986).
+    public var absoluteString: String {
+      return jsonReference.absoluteString
+    }
+}
+
+extension JSONDynamicReference {
+    private enum CodingKeys: String, CodingKey {
+        case dynamicRef = "$dynamicRef"
+    }
+}
+
+extension JSONDynamicReference: Encodable {
+    public func encode(to encoder: Encoder) throws {
+        var container = encoder.container(keyedBy: CodingKeys.self)
+
+        switch self.jsonReference {
+        case .internal(let reference):
+            try container.encode(reference.rawValue, forKey: .dynamicRef)
+        case .external(uri: let url):
+            try container.encode(url.absoluteString, forKey: .dynamicRef)
+        }
+    }
+}
+
+extension JSONDynamicReference: Decodable {
+    public init(from decoder: Decoder) throws {
+        let container = try decoder.container(keyedBy: CodingKeys.self)
+
+        let referenceString = try container.decode(String.self, forKey: .dynamicRef)
+
+        guard referenceString.count > 0 else {
+            throw DecodingError.dataCorruptedError(forKey: .dynamicRef, in: container, debugDescription: "Expected a reference string, but found an empty string instead.")
+        }
+
+        if referenceString.first == "#" {
+            guard let internalReference = InternalReference(rawValue: referenceString) else {
+                throw InconsistencyError(
+                    subjectName: "JSON Dynamic Reference",
+                    details: "Failed to parse a JSON Dynamic Reference from '\(referenceString)'",
+                    codingPath: container.codingPath
+                )
+            }
+            self = .internal(internalReference)
+        } else {
+            guard let externalReference = URL(string: referenceString) else {
+                throw InconsistencyError(
+                    subjectName: "JSON Dynamic Reference",
+                    details: "Failed to parse a valid URI for a JSON Dynamic Reference from '\(referenceString)'",
+                    codingPath: container.codingPath
+                )
+            }
+            self = .external(externalReference)
+        }
+    }
+}
diff --git a/Sources/OpenAPIKit/JSONReference.swift b/Sources/OpenAPIKit/JSONReference.swift
index 9d89efd83..5b4c65b73 100644
--- a/Sources/OpenAPIKit/JSONReference.swift
+++ b/Sources/OpenAPIKit/JSONReference.swift
@@ -393,6 +393,11 @@ public extension JSONReference {
             description: description
         )
     }
+
+    /// Create a dynamic JSON Reference from the given JSONReference.
+    var dynamicReference: JSONDynamicReference {
+      JSONDynamicReference(self)
+    }
 }
 
 /// `SummaryOverridable` exists to provide a parent protocol to `OpenAPIDescribable`

From 1def7b9c0d24ae49a5317af336f7b822b0648812 Mon Sep 17 00:00:00 2001
From: Mathew Polzin <matt.polzin@gmail.com>
Date: Tue, 19 Mar 2024 23:16:02 -0500
Subject: [PATCH 2/5] add dynamic references to json schemas

---
 Sources/OpenAPIKit/JSONDynamicReference.swift | 11 +--
 Sources/OpenAPIKit/JSONReference.swift        |  2 +
 .../DereferencedJSONSchema.swift              | 13 +++
 .../Schema Object/JSONSchema+Combining.swift  |  7 +-
 .../OpenAPIKit/Schema Object/JSONSchema.swift | 83 +++++++++++++++++--
 5 files changed, 104 insertions(+), 12 deletions(-)

diff --git a/Sources/OpenAPIKit/JSONDynamicReference.swift b/Sources/OpenAPIKit/JSONDynamicReference.swift
index b012a0387..416138943 100644
--- a/Sources/OpenAPIKit/JSONDynamicReference.swift
+++ b/Sources/OpenAPIKit/JSONDynamicReference.swift
@@ -6,6 +6,7 @@
 //
 
 import OpenAPIKitCore
+import Foundation
 
 @dynamicMemberLookup
 public struct JSONDynamicReference: Equatable, Hashable {
@@ -30,7 +31,7 @@ public struct JSONDynamicReference: Equatable, Hashable {
     ///     // encoded string: "#/components/schemas/greetings"
     ///     // Swift: `document.components.schemas["greetings"]`
     public static func component(named name: String) -> Self {
-      return .init(.internal(.component(name: name)), summary: summary, description: description)
+      return .init(.internal(.component(name: name)))
     }
 
     /// Reference a path internal to this file but not within the Components Object
@@ -39,7 +40,7 @@ public struct JSONDynamicReference: Equatable, Hashable {
     ///
     /// - Important: The path does not contain a leading '#'. Start with the root '/'.
     public static func `internal`(path: JSONReference<JSONSchema>.Path, summary: String? = nil, description: String? = nil) -> Self {
-      return .init(.internal(.path(path)), summary: summary, description: description)
+      return .init(.internal(.path(path)))
     }
 
     /// Reference an external URL.
@@ -104,14 +105,14 @@ extension JSONDynamicReference: Decodable {
         }
 
         if referenceString.first == "#" {
-            guard let internalReference = InternalReference(rawValue: referenceString) else {
+            guard let internalReference = JSONReference<JSONSchema>.InternalReference(rawValue: referenceString) else {
                 throw InconsistencyError(
                     subjectName: "JSON Dynamic Reference",
                     details: "Failed to parse a JSON Dynamic Reference from '\(referenceString)'",
                     codingPath: container.codingPath
                 )
             }
-            self = .internal(internalReference)
+            self = .init(.internal(internalReference))
         } else {
             guard let externalReference = URL(string: referenceString) else {
                 throw InconsistencyError(
@@ -120,7 +121,7 @@ extension JSONDynamicReference: Decodable {
                     codingPath: container.codingPath
                 )
             }
-            self = .external(externalReference)
+            self = .init(.external(externalReference))
         }
     }
 }
diff --git a/Sources/OpenAPIKit/JSONReference.swift b/Sources/OpenAPIKit/JSONReference.swift
index 5b4c65b73..4937ae2d1 100644
--- a/Sources/OpenAPIKit/JSONReference.swift
+++ b/Sources/OpenAPIKit/JSONReference.swift
@@ -393,7 +393,9 @@ public extension JSONReference {
             description: description
         )
     }
+}
 
+public extension JSONReference where ReferenceType == JSONSchema {
     /// Create a dynamic JSON Reference from the given JSONReference.
     var dynamicReference: JSONDynamicReference {
       JSONDynamicReference(self)
diff --git a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift
index 1ba5ba93a..72eb84ce5 100644
--- a/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift	
+++ b/Sources/OpenAPIKit/Schema Object/DereferencedJSONSchema.swift	
@@ -27,6 +27,7 @@ public enum DereferencedJSONSchema: Equatable, JSONSchemaContext {
     indirect case one(of: [DereferencedJSONSchema], core: CoreContext<JSONTypeFormat.AnyFormat>)
     indirect case any(of: [DereferencedJSONSchema], core: CoreContext<JSONTypeFormat.AnyFormat>)
     indirect case not(DereferencedJSONSchema, core: CoreContext<JSONTypeFormat.AnyFormat>)
+    case dynamicReference(JSONDynamicReference, CoreContext<JSONTypeFormat.AnyFormat>)
     /// Schemas without a `type`.
     case fragment(CoreContext<JSONTypeFormat.AnyFormat>) // This is the "{}" case where not even a type constraint is given.
 
@@ -65,6 +66,8 @@ public enum DereferencedJSONSchema: Equatable, JSONSchemaContext {
             return .any(of: schemas.map { $0.jsonSchema }, core: coreContext)
         case .not(let schema, core: let coreContext):
             return .not(schema.jsonSchema, core: coreContext)
+        case .dynamicReference(let reference, let coreContext):
+            return .dynamicReference(reference, coreContext)
         case .fragment(let context):
             return .fragment(context)
         }
@@ -96,6 +99,8 @@ public enum DereferencedJSONSchema: Equatable, JSONSchemaContext {
             return .any(of: schemas, core: core.optionalContext())
         case .not(let schema, core: let core):
             return .not(schema, core: core.optionalContext())
+        case .dynamicReference(let reference, let core):
+            return .dynamicReference(reference, core.optionalContext())
         }
     }
 
@@ -179,6 +184,8 @@ public enum DereferencedJSONSchema: Equatable, JSONSchemaContext {
             return coreContext.vendorExtensions
         case .not(_, core: let coreContext):
             return coreContext.vendorExtensions
+        case .dynamicReference(_, let coreContext):
+            return coreContext.vendorExtensions
         case .fragment(let context):
             return context.vendorExtensions
         }
@@ -209,6 +216,8 @@ public enum DereferencedJSONSchema: Equatable, JSONSchemaContext {
             return .any(of: schemas, core: coreContext.with(description: description))
         case .not(let schema, core: let coreContext):
             return .not(schema, core: coreContext.with(description: description))
+        case .dynamicReference(let reference, let coreContext):
+            return .dynamicReference(reference, coreContext.with(description: description))
         case .fragment(let context):
             return .fragment(context.with(description: description))
         }
@@ -239,6 +248,8 @@ public enum DereferencedJSONSchema: Equatable, JSONSchemaContext {
             return .any(of: schemas, core: coreContext.with(vendorExtensions: vendorExtensions))
         case .not(let schema, core: let coreContext):
             return .not(schema, core: coreContext.with(vendorExtensions: vendorExtensions))
+        case .dynamicReference(let reference, let coreContext):
+            return .dynamicReference(reference, coreContext.with(vendorExtensions: vendorExtensions))
         case .fragment(let context):
             return .fragment(context.with(vendorExtensions: vendorExtensions))
         }
@@ -520,6 +531,8 @@ extension JSONSchema: LocallyDereferenceable {
             return .any(of: schemas, core: addComponentNameExtension(to: coreContext))
         case .not(let jsonSchema, core: let coreContext):
             return .not(try jsonSchema._dereferenced(in: components, following: references, dereferencedFromComponentNamed: nil), core: addComponentNameExtension(to: coreContext))
+        case .dynamicReference(let reference, let coreContext):
+            return .dynamicReference(reference, addComponentNameExtension(to: coreContext))
         case .fragment(let context):
             return .fragment(addComponentNameExtension(to: context))
         }
diff --git a/Sources/OpenAPIKit/Schema Object/JSONSchema+Combining.swift b/Sources/OpenAPIKit/Schema Object/JSONSchema+Combining.swift
index a8f48b3a5..d461d5949 100644
--- a/Sources/OpenAPIKit/Schema Object/JSONSchema+Combining.swift	
+++ b/Sources/OpenAPIKit/Schema Object/JSONSchema+Combining.swift	
@@ -173,6 +173,8 @@ internal struct FragmentCombiner {
             self.combinedFragment = .array(try leftCoreContext.combined(with: rightCoreContext), arrayContext)
         case (.fragment(let leftCoreContext), .object(let rightCoreContext, let objectContext)):
             self.combinedFragment = .object(try leftCoreContext.combined(with: rightCoreContext), objectContext)
+        case (.fragment(let leftCoreContext), .dynamicReference(let reference, let rightCoreContext)):
+            self.combinedFragment = .dynamicReference(reference, try leftCoreContext.combined(with: rightCoreContext))
 
         case (.boolean(let leftCoreContext), .boolean(let rightCoreContext)):
             self.combinedFragment = .boolean(try leftCoreContext.combined(with: rightCoreContext))
@@ -208,7 +210,8 @@ internal struct FragmentCombiner {
              (.string, _),
              (.array, _),
              (.object, _),
-             (.null, _):
+             (.null, _),
+             (.dynamicReference, _):
             throw (
                 zip(combinedFragment.jsonType, fragment.jsonType).map {
                     JSONSchemaResolutionError(.typeConflict(original: $0, new: $1))
@@ -258,6 +261,8 @@ internal struct FragmentCombiner {
             jsonSchema = try .any(of: schemas, core: coreContext.validatedContext())
         case .one(of: let schemas, core: let coreContext):
             jsonSchema = try .one(of: schemas, core: coreContext.validatedContext())
+        case .dynamicReference(let reference, let coreContext):
+            jsonSchema = try .dynamicReference(reference, coreContext.validatedContext())
         case .not:
             throw JSONSchemaResolutionError(.unsupported(because: "`.not` is not yet supported for schema simplification"))
         }
diff --git a/Sources/OpenAPIKit/Schema Object/JSONSchema.swift b/Sources/OpenAPIKit/Schema Object/JSONSchema.swift
index 3c7477d37..a40e9020a 100644
--- a/Sources/OpenAPIKit/Schema Object/JSONSchema.swift	
+++ b/Sources/OpenAPIKit/Schema Object/JSONSchema.swift	
@@ -63,6 +63,9 @@ public struct JSONSchema: JSONSchemaContext, HasWarnings {
     public static func reference(_ reference: JSONReference<JSONSchema>, _ context: CoreContext<JSONTypeFormat.AnyFormat>) -> Self {
         .init(schema: .reference(reference, context))
     }
+    public static func dynamicReference(_ reference: JSONDynamicReference, _ context: CoreContext<JSONTypeFormat.AnyFormat>) -> Self {
+        .init(schema: .dynamicReference(reference, context))
+    }
     /// Schemas without a `type`.
     public static func fragment(_ core: CoreContext<JSONTypeFormat.AnyFormat>) -> Self {
         .init(schema: .fragment(core))
@@ -83,6 +86,7 @@ public struct JSONSchema: JSONSchemaContext, HasWarnings {
         indirect case any(of: [JSONSchema], core: CoreContext<JSONTypeFormat.AnyFormat>)
         indirect case not(JSONSchema, core: CoreContext<JSONTypeFormat.AnyFormat>)
         case reference(JSONReference<JSONSchema>, CoreContext<JSONTypeFormat.AnyFormat>)
+        case dynamicReference(JSONDynamicReference, CoreContext<JSONTypeFormat.AnyFormat>)
         /// Schemas without a `type`.
         case fragment(CoreContext<JSONTypeFormat.AnyFormat>) // This allows for the "{}" case and also fragments of schemas that will later be combined with `all(of:)`.
     }
@@ -104,7 +108,7 @@ public struct JSONSchema: JSONSchemaContext, HasWarnings {
             return .integer(context.format)
         case .string(let context, _):
             return .string(context.format)
-        case .all, .one, .any, .not, .reference, .fragment:
+        case .all, .one, .any, .not, .reference, .dynamicReference, .fragment:
             return nil
         }
     }
@@ -145,7 +149,7 @@ public struct JSONSchema: JSONSchemaContext, HasWarnings {
              .any(of: _, core: let context),
              .not(_, core: let context):
             return context.format.rawValue
-        case .reference, .null:
+        case .reference, .dynamicReference, .null:
             return nil
         }
     }
@@ -176,7 +180,7 @@ public struct JSONSchema: JSONSchemaContext, HasWarnings {
              .any(of: _, core: let context as JSONSchemaContext),
              .not(_, core: let context as JSONSchemaContext):
             return context.discriminator
-        case .reference:
+        case .reference, .dynamicReference:
             return nil
         }
     }
@@ -322,10 +326,20 @@ extension JSONSchema {
     }
 
     /// Check if a schema is a `.reference`.
+    ///
+    /// This returns `false` if the schema is a
+    /// `.dynamicReference` even though a
+    /// "dynamic reference" is a "reference".
     public var isReference: Bool {
         guard case .reference = value else { return false }
         return true
     }
+
+    /// Check if a schema is a `.dynamicReference`.
+    public var isDynamicReference: Bool {
+        guard case .reference = value else { return false }
+        return true
+    }
 }
 
 // MARK: - Context Accessors
@@ -347,7 +361,8 @@ extension JSONSchema {
              .one(of: _, core: let context as JSONSchemaContext),
              .any(of: _, core: let context as JSONSchemaContext),
              .not(_, core: let context as JSONSchemaContext),
-             .reference(_, let context as JSONSchemaContext):
+             .reference(_, let context as JSONSchemaContext),
+             .dynamicReference(_, let context as JSONSchemaContext):
             return context
         }
     }
@@ -476,6 +491,8 @@ extension JSONSchema.Schema {
             return .not(of, core: core.with(vendorExtensions: vendorExtensions))
         case .reference(let context, let coreContext):
             return .reference(context, coreContext.with(vendorExtensions: vendorExtensions))
+        case .dynamicReference(let context, let coreContext):
+            return .dynamicReference(context, coreContext.with(vendorExtensions: vendorExtensions))
         case .fragment(let context):
             return .fragment(context.with(vendorExtensions: vendorExtensions))
         }
@@ -547,6 +564,11 @@ extension JSONSchema {
                 warnings: warnings,
                 schema: .reference(reference, context.optionalContext())
             )
+        case .dynamicReference(let reference, let context):
+            return .init(
+                warnings: warnings,
+                schema: .dynamicReference(reference, context.optionalContext())
+            )
         case .null(let context):
             return .init(
                 warnings: warnings,
@@ -618,6 +640,11 @@ extension JSONSchema {
                 warnings: warnings,
                 schema: .reference(reference, context.requiredContext())
             )
+        case .dynamicReference(let reference, let context):
+            return .init(
+                warnings: warnings,
+                schema: .dynamicReference(reference, context.requiredContext())
+            )
         case .null(let context):
             return .init(
                 warnings: warnings,
@@ -684,7 +711,7 @@ extension JSONSchema {
                 warnings: warnings,
                 schema: .not(schema, core: core.nullableContext())
             )
-        case .reference, .null:
+        case .reference, .dynamicReference, .null:
             return self
         }
     }
@@ -753,6 +780,11 @@ extension JSONSchema {
                 warnings: warnings,
                 schema: .reference(schema, core.with(allowedValues: allowedValues))
             )
+        case .dynamicReference(let schema, let core):
+            return .init(
+                warnings: warnings,
+                schema: .dynamicReference(schema, core.with(allowedValues: allowedValues))
+            )
         case .null(let core):
             return .init(
                 warnings: warnings,
@@ -824,6 +856,11 @@ extension JSONSchema {
                 warnings: warnings,
                 schema: .reference(schema, core.with(defaultValue: defaultValue))
             )
+        case .dynamicReference(let schema, let core):
+            return .init(
+                warnings: warnings,
+                schema: .dynamicReference(schema, core.with(defaultValue: defaultValue))
+            )
         case .null(let core):
             return .init(
                 warnings: warnings,
@@ -902,6 +939,11 @@ extension JSONSchema {
                 warnings: warnings,
                 schema: .reference(schema, core.with(examples: examples))
             )
+        case .dynamicReference(let schema, let core):
+            return .init(
+                warnings: warnings,
+                schema: .dynamicReference(schema, core.with(examples: examples))
+            )
         case .null(let core):
             return .init(
                 warnings: warnings,
@@ -968,7 +1010,7 @@ extension JSONSchema {
                 warnings: warnings,
                 schema: .not(schema, core: core.with(discriminator: discriminator))
             )
-        case .reference, .null:
+        case .reference, .dynamicReference, .null:
             return self
         }
     }
@@ -1036,6 +1078,11 @@ extension JSONSchema {
                 warnings: warnings,
                 schema: .reference(ref, referenceContext.with(description: description))
             )
+        case .dynamicReference(let ref, let referenceContext):
+            return .init(
+                warnings: warnings,
+                schema: .dynamicReference(ref, referenceContext.with(description: description))
+            )
         case .null(let referenceContext):
             return .init(
                 warnings: warnings,
@@ -1757,6 +1804,21 @@ extension JSONSchema {
             .init(required: required, title: title, description: description, anchor: anchor, dynamicAnchor: dynamicAnchor)
         )
     }
+
+    /// Construct a dynamic reference schema
+    public static func dynamicReference(
+        _ reference: JSONDynamicReference,
+        required: Bool = true,
+        title: String? = nil,
+        description: String? = nil,
+        anchor: String? = nil,
+        dynamicAnchor: String? = nil
+    ) -> JSONSchema {
+        return .dynamicReference(
+            reference,
+            .init(required: required, title: title, description: description, anchor: anchor, dynamicAnchor: dynamicAnchor)
+        )
+    }
 }
 
 // MARK: - Describable
@@ -1852,6 +1914,10 @@ extension JSONSchema: Encodable {
             try core.encode(to: encoder)
             try reference.encode(to: encoder)
 
+        case .dynamicReference(let reference, let core):
+            try core.encode(to: encoder)
+            try reference.encode(to: encoder)
+
         case .fragment(let context):
             var container = encoder.singleValueContainer()
 
@@ -1890,6 +1956,11 @@ extension JSONSchema: Decodable {
             self = .init(warnings: coreContext.warnings, schema: .reference(ref, coreContext))
             return
         }
+        if let dynamicRef = try? JSONDynamicReference(from: decoder) {
+            let coreContext = try CoreContext<JSONTypeFormat.AnyFormat>(from: decoder)
+            self = .init(warnings: coreContext.warnings, schema: .dynamicReference(dynamicRef, coreContext))
+            return
+        }
 
         let container = try decoder.container(keyedBy: SubschemaCodingKeys.self)
 

From ed5575103cfd74816f750187c044b4af02d1e5a4 Mon Sep 17 00:00:00 2001
From: Mathew Polzin <matt.polzin@gmail.com>
Date: Sat, 23 Mar 2024 14:43:28 -0500
Subject: [PATCH 3/5] starting to stub out an example to test by. need defs to
 really use dynamicAnchors

---
 .../JSONSchemaDynamicReferenceTests.swift     | 25 +++++++++++++++++++
 1 file changed, 25 insertions(+)
 create mode 100644 Tests/OpenAPIKitTests/Schema Object/JSONSchemaDynamicReferenceTests.swift

diff --git a/Tests/OpenAPIKitTests/Schema Object/JSONSchemaDynamicReferenceTests.swift b/Tests/OpenAPIKitTests/Schema Object/JSONSchemaDynamicReferenceTests.swift
new file mode 100644
index 000000000..48ae0099c
--- /dev/null
+++ b/Tests/OpenAPIKitTests/Schema Object/JSONSchemaDynamicReferenceTests.swift	
@@ -0,0 +1,25 @@
+//
+//  JSONSchemaDynamicReferenceTests.swift
+//  
+//
+//  Created by Mathew Polzin.
+//
+
+import Foundation
+import XCTest
+import OpenAPIKit
+
+final class SchemaObjectDynamicReferenceTests: XCTestCase {
+    func test_tmp() throws {
+        let testComponents = OpenAPI.Components(
+            schemas: [
+                "T": .all(of: [], dynamicAnchor: "T"),
+                "genericList": .array(
+                    items: .dynamicReference(.internal("T"))
+                ),
+                "intList": .init(
+                )
+            ]
+        )
+    }
+}

From 7e71ce7422365c3d00ae9225595f8bcd32cf4e3e Mon Sep 17 00:00:00 2001
From: Mathew Polzin <matt.polzin@gmail.com>
Date: Sat, 23 Mar 2024 15:14:09 -0500
Subject: [PATCH 4/5] use schema-local defs

---
 .../JSONSchemaDynamicReferenceTests.swift              | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/Tests/OpenAPIKitTests/Schema Object/JSONSchemaDynamicReferenceTests.swift b/Tests/OpenAPIKitTests/Schema Object/JSONSchemaDynamicReferenceTests.swift
index 48ae0099c..18a6108cc 100644
--- a/Tests/OpenAPIKitTests/Schema Object/JSONSchemaDynamicReferenceTests.swift	
+++ b/Tests/OpenAPIKitTests/Schema Object/JSONSchemaDynamicReferenceTests.swift	
@@ -13,11 +13,17 @@ final class SchemaObjectDynamicReferenceTests: XCTestCase {
     func test_tmp() throws {
         let testComponents = OpenAPI.Components(
             schemas: [
-                "T": .all(of: [], dynamicAnchor: "T"),
                 "genericList": .array(
                     items: .dynamicReference(.internal("T"))
+                    defs: [
+                      "T": .all(of: [], dynamicAnchor: "T")
+                    ]
                 ),
-                "intList": .init(
+                "intList": .reference(
+                    .component(named: "genericList"), 
+                    defs: [
+                        "T": .string(dynamicAnchor: "T")
+                    ]
                 )
             ]
         )

From c4f9c06ed7248ef46c9a4c64fb6306b6fd2e78b9 Mon Sep 17 00:00:00 2001
From: Mathew Polzin <matt.polzin@gmail.com>
Date: Sun, 24 Mar 2024 12:45:59 -0500
Subject: [PATCH 5/5] support anchor types of references (as opposed to local
 paths or remote URIs)

---
 Sources/OpenAPIKit/JSONDynamicReference.swift | 10 +-
 Sources/OpenAPIKit/JSONReference.swift        | 10 ++
 .../JSONSchemaDynamicReferenceTests.swift     | 94 ++++++++++++++++++-
 3 files changed, 105 insertions(+), 9 deletions(-)

diff --git a/Sources/OpenAPIKit/JSONDynamicReference.swift b/Sources/OpenAPIKit/JSONDynamicReference.swift
index 416138943..8373c8665 100644
--- a/Sources/OpenAPIKit/JSONDynamicReference.swift
+++ b/Sources/OpenAPIKit/JSONDynamicReference.swift
@@ -34,13 +34,11 @@ public struct JSONDynamicReference: Equatable, Hashable {
       return .init(.internal(.component(name: name)))
     }
 
-    /// Reference a path internal to this file but not within the Components Object
-    /// This is likely not what you are looking for. It is advisable to store reusable components
-    /// in the Components Object.
+    /// Reference a dynamic anchor local to this file.
     ///
-    /// - Important: The path does not contain a leading '#'. Start with the root '/'.
-    public static func `internal`(path: JSONReference<JSONSchema>.Path, summary: String? = nil, description: String? = nil) -> Self {
-      return .init(.internal(.path(path)))
+    /// - Important: The anchor does not contain a leading '#'.
+    public static func anchor(_ anchor: String) -> Self {
+      return .init(.internal(.anchor(anchor)))
     }
 
     /// Reference an external URL.
diff --git a/Sources/OpenAPIKit/JSONReference.swift b/Sources/OpenAPIKit/JSONReference.swift
index 4937ae2d1..ab138a23d 100644
--- a/Sources/OpenAPIKit/JSONReference.swift
+++ b/Sources/OpenAPIKit/JSONReference.swift
@@ -129,6 +129,8 @@ public enum JSONReference<ReferenceType: ComponentDictionaryLocatable>: Equatabl
         case component(name: String)
         /// The reference refers to some path outside the Components Object.
         case path(Path)
+        /// The reference refers to some anchor anywhere in the local Schema.
+        case anchor(String)
 
         /// Get the name of the referenced object.
         ///
@@ -147,6 +149,8 @@ public enum JSONReference<ReferenceType: ComponentDictionaryLocatable>: Equatabl
                 return name
             case .path(let path):
                 return path.components.last?.stringValue
+            case .anchor(let name):
+                return name
             }
         }
 
@@ -164,6 +168,10 @@ public enum JSONReference<ReferenceType: ComponentDictionaryLocatable>: Equatabl
             }
             let fragment = rawValue.dropFirst()
             guard fragment.starts(with: "/components") else {
+                guard fragment.first == "/" else {
+                    self = .anchor(String(fragment))
+                    return
+                }
                 self = .path(Path(rawValue: String(fragment)))
                 return
             }
@@ -190,6 +198,8 @@ public enum JSONReference<ReferenceType: ComponentDictionaryLocatable>: Equatabl
                 return "#/components/\(ReferenceType.openAPIComponentsKey)/\(name)"
             case .path(let path):
                 return "#\(path.rawValue)"
+            case .anchor(let name):
+                return "#\(name)"
             }
         }
     }
diff --git a/Tests/OpenAPIKitTests/Schema Object/JSONSchemaDynamicReferenceTests.swift b/Tests/OpenAPIKitTests/Schema Object/JSONSchemaDynamicReferenceTests.swift
index 18a6108cc..a61fcf2d0 100644
--- a/Tests/OpenAPIKitTests/Schema Object/JSONSchemaDynamicReferenceTests.swift	
+++ b/Tests/OpenAPIKitTests/Schema Object/JSONSchemaDynamicReferenceTests.swift	
@@ -14,18 +14,106 @@ final class SchemaObjectDynamicReferenceTests: XCTestCase {
         let testComponents = OpenAPI.Components(
             schemas: [
                 "genericList": .array(
-                    items: .dynamicReference(.internal("T"))
+                    items: .dynamicReference(.anchor("T")),
                     defs: [
-                      "T": .all(of: [], dynamicAnchor: "T")
+                      "generic-param": .all(of: [], core: .init(dynamicAnchor: "T"))
                     ]
                 ),
                 "intList": .reference(
                     .component(named: "genericList"), 
                     defs: [
-                        "T": .string(dynamicAnchor: "T")
+                        "string-param": .string(dynamicAnchor: "T")
                     ]
                 )
             ]
         )
+
+        let jsonString = """
+        {
+          "schemas" : {
+            "genericList" : {
+              "$defs" : {
+                "T" : {
+                  "$dynamicAnchor" : "T",
+                  "allOf" : [
+
+                  ]
+                }
+              },
+              "items" : {
+                "$dynamicRef" : "#T"
+              },
+              "type" : "array"
+            },
+            "intList" : {
+              "$defs" : {
+                "T" : {
+                  "$dynamicAnchor" : "T",
+                  "type" : "string"
+                }
+              },
+              "$ref" : "#/components/schemas/genericList"
+            }
+          }
+        }
+        """
+
+        XCTAssertEqual(
+            testComponents, 
+            try orderUnstableDecode(OpenAPI.Components.self, from: jsonString.data(using: .utf8)!)
+        )
+    }
+
+    func test_tmp2() throws {
+        let testComponents = OpenAPI.Components(
+            schemas: [
+                "genericList": .array(
+                    items: .dynamicReference(.anchor("T")),
+                    defs: [
+                      "generic-param": .all(of: [], core: .init(dynamicAnchor: "T"))
+                    ]
+                ),
+                "intList": .reference(
+                    .component(named: "genericList"), 
+                    defs: [
+                        "string-param": .string(dynamicAnchor: "T")
+                    ]
+                )
+            ]
+        )
+
+        let testDoc = OpenAPI.Document(
+            info: .init(title: "test", version: "1.0.0"),
+            servers: [],
+            paths: [:],
+            components: testComponents
+        )
+
+        let dereferenced = try testDoc.locallyDereferenced()
+
+        print(dereferenced.components.schemas["intList"]!.defs)
+    }
+
+    func buildDynamicContext<T>(_ defs: OrderedDictionary<T, JSONSchema>) -> [String: JSONSchema] {
+        var dynamicContext: [String: JSONSchema] = [:]
+        for (_, def) in defs {
+          if let anchor = def.dynamicAnchor {
+            dynamicContext[anchor] = def
+          }
+        }
+        return dynamicContext
+    }
+
+    func t(_ source: JSONSchema, _ components: OpenAPI.Components) throws -> JSONSchema {
+        return try tp(
+            source,
+            dynamicContext: buildDynamicContext(components.schemas)
+        )
+    }
+
+    func tp(_ source: JSONSchema, dynamicContext: [String: JSONSchema]) throws -> JSONSchema {
+        var localDynamicContext = buildDynamicContext(source.defs)
+
+        localDynamicContext.merge(dynamicContext, uniquingKeysWith: { local, global in local })
     }
 }