import type JSBI from 'jsbi'; import type { Temporal } from '..'; import type { BuiltinCalendarId, AnyTemporalType, CalendarSlot, TimeZoneSlot } from './internaltypes'; // Instant export const EPOCHNANOSECONDS = 'slot-epochNanoSeconds'; // TimeZone export const TIMEZONE_ID = 'slot-timezone-identifier'; // DateTime, Date, Time, YearMonth, MonthDay export const ISO_YEAR = 'slot-year'; export const ISO_MONTH = 'slot-month'; export const ISO_DAY = 'slot-day'; export const ISO_HOUR = 'slot-hour'; export const ISO_MINUTE = 'slot-minute'; export const ISO_SECOND = 'slot-second'; export const ISO_MILLISECOND = 'slot-millisecond'; export const ISO_MICROSECOND = 'slot-microsecond'; export const ISO_NANOSECOND = 'slot-nanosecond'; export const CALENDAR = 'slot-calendar'; // Date, YearMonth, and MonthDay all have the same slots, disambiguation needed: export const DATE_BRAND = 'slot-date-brand'; export const YEAR_MONTH_BRAND = 'slot-year-month-brand'; export const MONTH_DAY_BRAND = 'slot-month-day-brand'; // ZonedDateTime export const INSTANT = 'slot-cached-instant'; export const TIME_ZONE = 'slot-time-zone'; // Duration export const YEARS = 'slot-years'; export const MONTHS = 'slot-months'; export const WEEKS = 'slot-weeks'; export const DAYS = 'slot-days'; export const HOURS = 'slot-hours'; export const MINUTES = 'slot-minutes'; export const SECONDS = 'slot-seconds'; export const MILLISECONDS = 'slot-milliseconds'; export const MICROSECONDS = 'slot-microseconds'; export const NANOSECONDS = 'slot-nanoseconds'; // Calendar export const CALENDAR_ID = 'slot-calendar-identifier'; interface SlotInfo { value: ValueType; usedBy: UsedByType; } interface SlotInfoRecord { [k: string]: SlotInfo; } interface Slots extends SlotInfoRecord { // Instant [EPOCHNANOSECONDS]: SlotInfo; // number? JSBI? // TimeZone [TIMEZONE_ID]: SlotInfo; // DateTime, Date, Time, YearMonth, MonthDay [ISO_YEAR]: SlotInfo; [ISO_MONTH]: SlotInfo; [ISO_DAY]: SlotInfo; [ISO_HOUR]: SlotInfo; [ISO_MINUTE]: SlotInfo; [ISO_SECOND]: SlotInfo; [ISO_MILLISECOND]: SlotInfo; [ISO_MICROSECOND]: SlotInfo; [ISO_NANOSECOND]: SlotInfo; [CALENDAR]: SlotInfo; // Date, YearMonth, MonthDay common slots [DATE_BRAND]: SlotInfo; [YEAR_MONTH_BRAND]: SlotInfo; [MONTH_DAY_BRAND]: SlotInfo; // ZonedDateTime [INSTANT]: SlotInfo; [TIME_ZONE]: SlotInfo; // Duration [YEARS]: SlotInfo; [MONTHS]: SlotInfo; [WEEKS]: SlotInfo; [DAYS]: SlotInfo; [HOURS]: SlotInfo; [MINUTES]: SlotInfo; [SECONDS]: SlotInfo; [MILLISECONDS]: SlotInfo; [MICROSECONDS]: SlotInfo; [NANOSECONDS]: SlotInfo; // Calendar [CALENDAR_ID]: SlotInfo; } type TypesWithCalendarUnits = | Temporal.PlainDateTime | Temporal.PlainDate | Temporal.PlainTime | Temporal.PlainYearMonth | Temporal.PlainMonthDay | Temporal.ZonedDateTime; interface SlotsToTypes { // Instant [EPOCHNANOSECONDS]: Temporal.Instant; // TimeZone [TIMEZONE_ID]: Temporal.TimeZone; // DateTime, Date, Time, YearMonth, MonthDay [ISO_YEAR]: TypesWithCalendarUnits; [ISO_MONTH]: TypesWithCalendarUnits; [ISO_DAY]: TypesWithCalendarUnits; [ISO_HOUR]: TypesWithCalendarUnits; [ISO_MINUTE]: TypesWithCalendarUnits; [ISO_SECOND]: TypesWithCalendarUnits; [ISO_MILLISECOND]: TypesWithCalendarUnits; [ISO_MICROSECOND]: TypesWithCalendarUnits; [ISO_NANOSECOND]: TypesWithCalendarUnits; [CALENDAR]: TypesWithCalendarUnits; // Date, YearMonth, MonthDay common slots [DATE_BRAND]: Temporal.PlainDate; [YEAR_MONTH_BRAND]: Temporal.PlainYearMonth; [MONTH_DAY_BRAND]: Temporal.PlainMonthDay; // ZonedDateTime [INSTANT]: Temporal.ZonedDateTime; [TIME_ZONE]: Temporal.ZonedDateTime; // Duration [YEARS]: Temporal.Duration; [MONTHS]: Temporal.Duration; [WEEKS]: Temporal.Duration; [DAYS]: Temporal.Duration; [HOURS]: Temporal.Duration; [MINUTES]: Temporal.Duration; [SECONDS]: Temporal.Duration; [MILLISECONDS]: Temporal.Duration; [MICROSECONDS]: Temporal.Duration; [NANOSECONDS]: Temporal.Duration; // Calendar [CALENDAR_ID]: Temporal.Calendar; } type SlotKey = keyof SlotsToTypes; const globalSlots = new WeakMap>(); function _GetSlots(container: Slots[keyof Slots]['usedBy']) { return globalSlots.get(container); } const GetSlotsSymbol = Symbol.for('@@Temporal__GetSlots'); // expose GetSlots to avoid dual package hazards (globalThis as any)[GetSlotsSymbol] ||= _GetSlots; const GetSlots = (globalThis as any)[GetSlotsSymbol] as typeof _GetSlots; function _CreateSlots(container: Slots[keyof Slots]['usedBy']): void { globalSlots.set(container, Object.create(null)); } const CreateSlotsSymbol = Symbol.for('@@Temporal__CreateSlots'); // expose CreateSlots to avoid dual package hazards (globalThis as any)[CreateSlotsSymbol] ||= _CreateSlots; export const CreateSlots = (globalThis as any)[CreateSlotsSymbol] as typeof _CreateSlots; // TODO: is there a better way than 9 overloads to make HasSlot into a type // guard that takes a variable number of parameters? export function HasSlot(container: unknown, id1: ID1): container is Slots[ID1]['usedBy']; export function HasSlot( container: unknown, id1: ID1, id2: ID2 ): container is Slots[ID1]['usedBy'] | Slots[ID2]['usedBy']; export function HasSlot( container: unknown, id1: ID1, id2: ID2, id3: ID3 ): container is Slots[ID1]['usedBy'] | Slots[ID2]['usedBy'] | Slots[ID3]['usedBy']; export function HasSlot( container: unknown, id1: ID1, id2: ID2, id3: ID3, id4: ID4 ): container is Slots[ID1 | ID2 | ID3 | ID4]['usedBy']; export function HasSlot< ID1 extends SlotKey, ID2 extends SlotKey, ID3 extends SlotKey, ID4 extends SlotKey, ID5 extends SlotKey >( container: unknown, id1: ID1, id2: ID2, id3: ID3, id4: ID4, id5: ID5 ): container is Slots[ID1 | ID2 | ID3 | ID4 | ID5]['usedBy']; export function HasSlot< ID1 extends SlotKey, ID2 extends SlotKey, ID3 extends SlotKey, ID4 extends SlotKey, ID5 extends SlotKey, ID6 extends SlotKey >( container: unknown, id1: ID1, id2: ID2, id3: ID3, id4: ID4, id5: ID5, id6: ID6 ): container is Slots[ID1 | ID2 | ID3 | ID4 | ID5 | ID6]['usedBy']; export function HasSlot< ID1 extends SlotKey, ID2 extends SlotKey, ID3 extends SlotKey, ID4 extends SlotKey, ID5 extends SlotKey, ID6 extends SlotKey, ID7 extends SlotKey >( container: unknown, id1: ID1, id2: ID2, id3: ID3, id4: ID4, id5: ID5, id6: ID6, id7: ID7 ): container is Slots[ID1 | ID2 | ID3 | ID4 | ID5 | ID6 | ID7]['usedBy']; export function HasSlot< ID1 extends SlotKey, ID2 extends SlotKey, ID3 extends SlotKey, ID4 extends SlotKey, ID5 extends SlotKey, ID6 extends SlotKey, ID7 extends SlotKey, ID8 extends SlotKey >( container: unknown, id1: ID1, id2: ID2, id3: ID3, id4: ID4, id5: ID5, id6: ID6, id7: ID7, id8: ID8 ): container is Slots[ID1 | ID2 | ID3 | ID4 | ID5 | ID6 | ID7 | ID8]['usedBy']; export function HasSlot< ID1 extends SlotKey, ID2 extends SlotKey, ID3 extends SlotKey, ID4 extends SlotKey, ID5 extends SlotKey, ID6 extends SlotKey, ID7 extends SlotKey, ID8 extends SlotKey, ID9 extends SlotKey >( container: unknown, id1: ID1, id2: ID2, id3: ID3, id4: ID4, id5: ID5, id6: ID6, id7: ID7, id8: ID8, id9: ID9 ): container is Slots[ID1 | ID2 | ID3 | ID4 | ID5 | ID6 | ID7 | ID8 | ID9]['usedBy']; export function HasSlot(container: unknown, ...ids: (keyof Slots)[]): boolean { if (!container || 'object' !== typeof container) return false; const myslots = GetSlots(container as AnyTemporalType); return !!myslots && ids.every((id) => id in myslots); } export function GetSlot( container: Slots[typeof id]['usedBy'], id: KeyT ): Slots[KeyT]['value'] { const value = GetSlots(container)?.[id]; if (value === undefined) throw new TypeError(`Missing internal slot ${id}`); return value; } export function SetSlot( container: Slots[KeyT]['usedBy'], id: KeyT, value: Slots[KeyT]['value'] ): void { const slots = GetSlots(container); if (slots === undefined) throw new TypeError('Missing slots for the given container'); const existingSlot = slots[id]; if (existingSlot) throw new TypeError(`${id} already has set`); slots[id] = value; }