			"TraversalError: must be a valid object JSON Schema (was an object JSON Schema with 'required' array but no 'properties' object)"
		)
		attest(() =>
			jsonSchemaToType({
				type: "object",
				properties: { foo: { type: "string" } },
				required: ["bar"]
			})
		).throws(
			`TraversalError: required must be a key from the 'properties' object, i.e. foo (was bar)`
		)
		attest(() =>
			jsonSchemaToType({
				type: "object",
				properties: { foo: { type: "string" } },
				required: ["foo", "foo"]
			})
		).throws(writeDuplicateKeyMessage("foo"))
	})

	it("additionalProperties", () => {
		const tAdditionalProperties = jsonSchemaToType({
			type: "object",
			additionalProperties: { type: "number" },
			properties: { bar: { type: "string" } }
		})
		attest(tAdditionalProperties.json).snap({
			domain: "object",
			optional: [{ key: "bar", value: "string" }],
			predicate: ["$ark.jsonSchemaObjectAdditionalPropertiesValidator"]
		})
		attest(tAdditionalProperties.allows({})).equals(true)
		attest(tAdditionalProperties.allows({ foo: 1 })).equals(true)
		attest(tAdditionalProperties.allows({ foo: 1, bar: "2" })).equals(true)
		attest(tAdditionalProperties.allows({ foo: 1, baz: "2" })).equals(false)
	})

	it("patternProperties", () => {
		const tPatternProperties = jsonSchemaToType({
			type: "object",
			patternProperties: {
				"^[a-z]+$": { type: "string" }
			}
		})
		attest(tPatternProperties.expression).snap("{ [/^[a-z]+$/]: string }")
		attest(tPatternProperties.allows({})).equals(true)
		attest(tPatternProperties.allows({ foo: "bar" })).equals(true)
		attest(tPatternProperties.allows({ foo: 1 })).equals(false)
		attest(tPatternProperties.allows({ "123": "bar" })).equals(true) // true since by default JSON Schema allows additional properties
	})

	it("propertyNames", () => {
		const tPropertyNames = jsonSchemaToType({
			type: "object",
			propertyNames: { type: "string", minLength: 5 }
		})
		attest(tPropertyNames.expression).snap(
			"{ [string >= 5]: unknown, + (undeclared): reject }"
		)

		attest(() =>
			// @ts-expect-error
			jsonSchemaToType({
				type: "object",
				propertyNames: { type: "number" }
			})
		).type.errors.snap(
			`Argument of type '{ type: "object"; propertyNames: { type: "number"; }; }' is not assignable to parameter of type 'JsonSchemaOrBoolean'.` +
				`The types of 'propertyNames.type' are incompatible between these types.` +
				`Type '"number"' is not assignable to type '"string"'.`
		)
	})

	it("propertyNames & additionalProperties", () => {
		const tPropertyNamesAndAdditionalProperties = jsonSchemaToType({
			type: "object",
			propertyNames: { type: "string", minLength: 3 },
			additionalProperties: true
		})
		attest(tPropertyNamesAndAdditionalProperties.expression).snap(
			"{ [string >= 3]: unknown, + (undeclared): reject }"
