blob: 88565480b47989873f3def3726c6f014a62f9042 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// See ../PRESUBMIT.py.
import type * as old from '@tmp/old_glic_api.js';
import type * as current from '../glic_api/glic_api.js';
// Warning! The checks in this file are not a complete guarantee of API
// compatibility.
/* eslint-disable-next-line @typescript-eslint/naming-convention */
function assertNever<_T extends never>() {}
type AllValues<T> = T[keyof T];
// TODO: This doesn't recurse into function parameter or return types, which
// means we're missing compatibility checks for parameter/return objects
// declared inline.
type DeepRequired<T> = {
[K in keyof T]: DeepRequired<T[K]>
}&Required<T>;
// Get the set of BackwardsCompatibleTypes in both old and current. This
// allows us to ignore types removed from BackwardsCompatibleTypes.
type OldTypes = {
[K in keyof old.BackwardsCompatibleTypes &
keyof current.BackwardsCompatibleTypes]: old.BackwardsCompatibleTypes[K]
};
type CurrentTypes = {
[K in keyof old.BackwardsCompatibleTypes &
keyof current.BackwardsCompatibleTypes]: current.BackwardsCompatibleTypes[K]
};
/*
These are the kinds of changes we might see, and how they're categorized.
* OK:
* Adding an optional field. {x:number} --> {x:number; y?: string}
* Adding an optional parameter. foo():void -> foo(x?:number)
* ERROR:
* Removing any field. {x?:number} --> {}
* Adding a required field. {} --> {x:number}
* Adding a required parameter. foo():void -> foo(x:number)
* Widening a field type. {x:number} --> {x:number|string}
(Changing a host type in this way is likely not compatible for old versions
of Chrome.)
*/
// Note: We're just using assignment to verify these types are compatible.
export const oldTypesAreCompatibleWithCurrent: CurrentTypes =
null as any as old.BackwardsCompatibleTypes;
export const currentTypesAreCompatibleWithOld: OldTypes =
null as any as current.BackwardsCompatibleTypes;
// Make all fields required, then check that all fields are compatible. This
// ensures we don't remove optional fields.
export const canNotRemoveAnything: DeepRequired<OldTypes> =
null as any as DeepRequired<current.BackwardsCompatibleTypes>;
// Ensure ClosedEnums are not modified, and ExtensibleEnums are only extended.
// TODO: This only checks enum keys. Not sure how to check values.
type EnumOnlyExtended<O, N> = Exclude<keyof O, keyof N> extends never ?
never :
['Error: enum changed', O];
type EnumIsEquivalent<O, N> = Exclude<keyof N, keyof O> extends never ?
EnumOnlyExtended<O, N>:
['Error: enum changed', O];
type ClosedEnumsDoNotChange = AllValues<{
[K in keyof current.ClosedEnums & keyof old.ClosedEnums]:
EnumIsEquivalent<old.ClosedEnums[K], current.ClosedEnums[K]>;
}>;
assertNever<ClosedEnumsDoNotChange>();
type CheckExtensibleEnums = AllValues<{
[K in keyof current.ExtensibleEnums & keyof old.ExtensibleEnums]:
EnumOnlyExtended<old.ExtensibleEnums[K], current.ExtensibleEnums[K]>;
}>;
assertNever<CheckExtensibleEnums>();