New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@pexip-engage-public/plugin

Package Overview
Dependencies
Maintainers
1
Versions
239
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@pexip-engage-public/plugin - npm Package Compare versions

Comparing version

to
1.0.17

dist/configuration-parser/migrate-legacy-configuration.d.ts

6

CHANGELOG.md
# @pexip-engage-public/plugin
## 1.0.17
### Patch Changes
- b7a5179: feat: cleanup plugin integration
## 1.0.16

@@ -4,0 +10,0 @@

7

dist/configuration-parser-legacy/LegacyParser.utils.js

@@ -1,3 +0,8 @@

import { DEPRECATED_TOKEN, TOKEN } from "../constants.js";
import { TOKEN } from "../constants.js";
import { sanitize } from "./utils.js";
const DEPRECATED_TOKEN = {
LOCK: "#",
OFFICE: "L",
START: "'",
};
export function legacyHashToPluginInputConfiguration(config) {

@@ -4,0 +9,0 @@ return sanitize({

2

dist/configuration-parser/hash-parser.d.ts

@@ -5,4 +5,4 @@ import type { PluginConfiguration } from "../configuration/PluginConfiguration.current.js";

encode(value: PluginConfiguration): string;
decode(hash?: string | null): PluginConfiguration;
decode(hash?: string | null): PluginConfiguration | null;
};
//# sourceMappingURL=hash-parser.d.ts.map

@@ -9,3 +9,3 @@ import { decodeURIParameters, encodeURIParameters } from "../encoding.js";

if (!hash || !hash.startsWith(HASH_PREFIX))
return {};
return null;
const value = hash.slice(HASH_PREFIX.length);

@@ -12,0 +12,0 @@ const configuration = decodeURIParameters(value);

@@ -8,4 +8,4 @@ import type { PluginConfiguration } from "../configuration/PluginConfiguration.current.js";

encode(config: PluginConfiguration): string[];
decode(element?: HTMLElement | null): PluginConfiguration;
decode(element?: HTMLElement | null): PluginConfiguration | null;
};
//# sourceMappingURL=html-parser.d.ts.map

@@ -10,3 +10,3 @@ import { logger } from "../logger.js";

if (!element)
return {};
return null;
const attributes = element.getAttributeNames().filter((name) => name.startsWith(ATTR_PREFIX));

@@ -23,3 +23,3 @@ const object = {};

logger.error("Failed to parse HTML attributes", err);
return {};
return null;
}

@@ -26,0 +26,0 @@ },

import type { PluginConfiguration } from "../configuration/PluginConfiguration.current.js";
import { type JSConfig } from "./js-parser.js";
export declare const CONFIG_VERSION: Exclude<PluginConfiguration["version"], undefined>;
export declare const parser: {
js: {
encode(config: PluginConfiguration): JSConfig;
decode(jsConfig?: unknown): PluginConfiguration;
encode(config: PluginConfiguration): import("./js-parser.js").JSConfig;
decode(jsConfig?: unknown): PluginConfiguration | null;
};
hash: {
encode(value: PluginConfiguration): string;
decode(hash?: string | null | undefined): PluginConfiguration;
decode(hash?: string | null | undefined): PluginConfiguration | null;
};
html: {
encode(config: PluginConfiguration): string[];
decode(element?: HTMLElement | null | undefined): PluginConfiguration;
decode(element?: HTMLElement | null | undefined): PluginConfiguration | null;
};

@@ -21,3 +20,3 @@ };

export type { JSConfig } from "./js-parser.js";
interface ConfigurationProps {
export interface ConfigurationProps {
element?: HTMLElement;

@@ -33,12 +32,2 @@ hash?: string;

config: PluginConfiguration;
domConfig: PluginConfiguration;
hashConfig: PluginConfiguration;
jsConfig: PluginConfiguration;
} | {
migrationInstructions: {
dom: string | null;
hash: string | null;
js: JSConfig | null;
};
config: PluginConfiguration;
domConfig: PluginConfiguration | null;

@@ -45,0 +34,0 @@ hashConfig: PluginConfiguration | null;

@@ -1,3 +0,1 @@

import { migrateConfiguration } from "../configuration/migration.js";
import { parsePluginConfigurationV1 } from "../configuration-parser-legacy/Parser.js";
import { logger } from "../logger.js";

@@ -7,2 +5,3 @@ import { hashParser } from "./hash-parser.js";

import { jsParser } from "./js-parser.js";
// import { migrateLegacyConfiguration } from './migrate-legacy-configuration.js';
export const CONFIG_VERSION = "1.0.0";

@@ -15,2 +14,7 @@ export const parser = { js: jsParser, hash: hashParser, html: htmlParser };

const version = result.config.version;
if (!version) {
logger.warn(`Failed to detect config version, assuming latest version.
Please add a version to your config to avoid breaking it in the future.
Current version: ${CONFIG_VERSION}`);
}
// only one version for now, can just return it.

@@ -20,12 +24,4 @@ if (version === CONFIG_VERSION) {

}
const v1Config = parsePluginConfigurationV1({
element: args.element,
hash: args.hash,
href: args.browser.href,
browserTimeZone: args.browser.timeZone,
options: args.options,
});
const migrated = createMigration(v1Config);
// migrated.
return migrated;
return result;
// return migrateLegacyConfiguration(args);
}

@@ -45,42 +41,2 @@ function parse({ browser, element, hash, options }) {

}
function createMigration(result) {
const data = {
config: migrateConfiguration(result.config) ?? { version: "1.0.0" },
domConfig: migrateConfiguration(result.domConfig),
hashConfig: migrateConfiguration(result.hashConfig),
jsConfig: migrateConfiguration(result.jsConfig),
};
const migrationInstructions = {
dom: data.domConfig
? `<div class="pexip-engage-plugin"\n ${parser.html
.encode(data.domConfig)
.join("\n ")}\n></div>`
: null,
hash: data.hashConfig ? parser.hash.encode(data.hashConfig) : null,
js: data.jsConfig ? parser.js.encode(data.jsConfig) : null,
};
logDeprecationWarning(migrationInstructions);
return {
...data,
migrationInstructions,
};
}
function logDeprecationWarning({ dom, hash, js, }) {
if (dom || hash || js) {
logger.group("DEPRECATED CONFIGURATION");
if (dom) {
try {
logger.warn("Detected unsupported DOM config. Please update your DOM element to:\n", dom.replaceAll("\n ", ""));
}
catch (err) { }
}
if (hash) {
logger.warn("Detected deprecated hash config. Please update your hash to:\n%o", hash);
}
if (js) {
logger.warn("Detected deprecated JS config. Please update your JS to:\n%o", `PexipEngage.Plugin(element, ${JSON.stringify(js)})`);
}
logger.groupEnd();
}
}
//# sourceMappingURL=index.js.map

@@ -0,5 +1,6 @@

import type { MeetingType } from "@pexip-engage-public/graphql";
import type { PluginConfiguration } from "../configuration/PluginConfiguration.current.js";
export declare const jsParser: {
encode(config: PluginConfiguration): JSConfig;
decode(jsConfig?: unknown): PluginConfiguration;
decode(jsConfig?: unknown): PluginConfiguration | null;
};

@@ -11,13 +12,50 @@ type StringConfig = string | null;

config?: {
version: "1.0.0";
version?: "1.0.0";
/**
* A sequence of characters that indicates the order of steps to be shown. The following characters are supported:
* - `S`: Subject. The step that allows the customer to pick a subject.
* - `O`: Office. The step that allows the customer to pick an office.
* - `M`: Meeting type. The step that allows the customer to pick a meeting type.
* - `E`: Employee. The step that allows the customer to pick an employee.
* - `T`: Timetable. The step that allows the customer to suggest one or more time slots for their appointment.
* - `Q`: Questions. The step that allows the customer to answer any addition questions specific to the chosen subject.
* - `C`: Customer. The step that allows the customer to provide his/her contact information.
* - `*`: [locked]. Indicate that the step is locked and can not be edited. This means that a value must be provided.
*
* Currently, the following constraints must always be met:
* - The following characters must always be present: `T`, `Q`, `C`.
* - No other characters than those listed above may be used.
* - When `S` is not present, it is mandatory that an external mechanism will provide the `config.subject.id` + `config.subject.type` configuration option instead.
* - When `O` is not present, it is mandatory that an external mechanism will provide the `config.office.id` + `config.office.type`configuration option instead.
* - If `S` or `O` are present, they can never occur at a position after `T`.
* - If `S` is present, it can never occur at a position after `Q`.
*
* By default the value `SMOTQC` will be assumed. This will be the case if no value was provided or if the provided value fails these constraints.
* @default "SMOTQC"
*/
flow?: StringConfig;
intent?: StringConfig;
language?: StringConfig;
intent?: "schedule" | "reschedule" | "invite" | "edit" | "cancel" | "complete" | null;
/**
* An RFC 5646-compatible language tag.
*
* This will cause the plugin to be displayed in the specified language, unless the language is unavailable.
*
* In that case, a best-effort fallback will be performed with increasingly more generic variants of the specified language or finally 'en' if no variants of the specified language are available.
*/
language?: "da" | "de" | "el" | "en" | "en-GB" | "en-US" | "es" | "fr" | "no" | "nl" | "pl" | null;
employee?: {
ids?: StringArrayConfig;
type?: StringConfig;
type?: "id" | "externalId" | null;
};
application?: {
/**
* Used to set scroll top value when navigating steps
* @default -90
*/
scroll?: "disabled" | number | null;
timezoneSelection?: StringConfig;
/**
* Used to enable timezone selection when the meeting type is video or phone.
* @default "enable"
*/
timeZoneSelection?: "enable" | "disable" | null;
theme?: string | null;

@@ -27,38 +65,40 @@ };

company?: StringConfig;
customer_number?: StringConfig;
date_of_birth?: StringConfig;
customerNumber?: StringConfig;
email?: StringConfig;
external_id?: StringConfig;
first_name?: StringConfig;
gender?: StringConfig;
existing?: BooleanConfig;
externalId?: StringConfig;
firstName?: StringConfig;
id?: StringConfig;
is_existing?: BooleanConfig;
language?: StringConfig;
last_name?: StringConfig;
lastName?: StringConfig;
location?: {
city?: StringConfig;
country?: StringConfig;
geolocation?: StringConfig;
postal_code?: StringConfig;
countryCode?: StringConfig;
geolocation?: {
latitude: StringConfig;
longitude: StringConfig;
};
postalCode?: StringConfig;
timeZone?: StringConfig;
state?: StringConfig;
street_1?: StringConfig;
street1?: StringConfig;
street2?: StringConfig;
};
phone_number?: StringConfig;
preferred_contact_id?: StringConfig;
preferred_office_id?: StringConfig;
timezone?: StringConfig;
phoneNumber?: StringConfig;
timeZone?: StringConfig;
};
leadSegment?: {
id?: StringConfig;
type?: StringConfig;
type?: "id" | "code" | null;
};
listing?: {
id?: StringConfig;
type?: StringConfig;
type?: "id" | "externalId" | null;
};
location?: {
initialSearch?: StringConfig;
formattedAddress?: StringConfig;
};
meetingTypes?: StringArrayConfig;
meetingTypes?: MeetingType[] | MeetingType | null;
oauth?: {
/** A resource_code of an existing appointment. This will allow you to complete or change an appointment. This parameter is mostly used by redirects from external sources (e.g. e-mails) and should usually not be provided manually. */
resourceCode?: StringConfig;

@@ -68,16 +108,24 @@ };

ids?: StringArrayConfig;
type?: StringConfig;
type?: "id" | "externalId" | null;
};
/**
* The country that should be used for searching locations. Should be a two letter ISO 3166-1 country code.
*
* You can provide a maximum of 5 comma-separated countries.
*/
searchCountries?: StringArrayConfig;
session?: {
id?: StringConfig;
/** Pass source tags seperated with a semi-colon. */
sourceTags?: StringArrayConfig;
status?: StringConfig;
/** Used to enable/disable tracking of events in the plugin for insights about conversion. @default "default" */
status?: "default" | "disabled" | null;
};
subject?: {
ids?: StringArrayConfig;
type?: StringConfig;
type?: "id" | "externalId" | null;
};
timetable?: {
startDate?: StringConfig;
subjectGroup?: {
ids?: StringArrayConfig;
type?: "id" | "externalId" | null;
};

@@ -84,0 +132,0 @@ };

@@ -1,18 +0,87 @@

import { isValidConfiguration } from "../configuration-parser-legacy/utils.js";
import { logger } from "../logger.js";
function isValidConfiguration(configuration) {
return typeof configuration === "object" && configuration !== null;
}
export const jsParser = {
encode(config) {
//@ts-ignore
const result = {};
for (let key in config) {
const value = config[key];
const keys = key.split("-");
//@ts-ignore
setValue(result, keys, value);
}
return { config: result };
const result = {
version: config.version,
flow: config.flow,
intent: config.intent,
language: config.language,
employee: {
ids: config["employee-ids"],
type: config["employee-type"],
},
application: {
scroll: config["application-scroll"],
timeZoneSelection: config["application-time_zone_selection"],
// timetable_ui: config["application-timetable_ui"],
theme: config["application-theme"],
},
customer: {
company: config["customer-company"],
customerNumber: config["customer-customer_number"],
email: config["customer-email"],
existing: config["customer-existing"],
externalId: config["customer-external_id"],
firstName: config["customer-first_name"],
id: config["customer-id"],
language: config["customer-language"],
lastName: config["customer-last_name"],
location: {
city: config["customer-location-city"],
countryCode: config["customer-location-country_code"],
geolocation: {
latitude: config["customer-location-geolocation-latitude"],
longitude: config["customer-location-geolocation-longitude"],
},
postalCode: config["customer-location-postal_code"],
timeZone: config["customer-location-time_zone"],
state: config["customer-location-state"],
street1: config["customer-location-street1"],
street2: config["customer-location-street2"],
},
phoneNumber: config["customer-phone_number"],
timeZone: config["customer-time_zone"],
},
leadSegment: {
id: config["lead_segment-id"],
type: config["lead_segment-type"],
},
listing: {
id: config["listing-id"],
type: config["listing-type"],
},
location: {
formattedAddress: config["location-formatted_address"],
},
meetingTypes: config["meeting_types"],
oauth: {
resourceCode: config["oauth-resource_code"],
},
office: {
ids: config["office-ids"],
type: config["office-type"],
},
searchCountries: config["search_countries"],
session: {
id: config["session-id"],
sourceTags: config["session-source_tags"],
status: config["session-status"],
},
subject: {
ids: config["subject-ids"],
type: config["subject-type"],
},
subjectGroup: {
ids: config["subject_group-ids"],
type: config["subject_group-type"],
},
};
return { config: prune(result) };
},
decode(jsConfig) {
if (!jsConfig)
return {};
return null;
try {

@@ -22,6 +91,56 @@ if (!isValidConfiguration(jsConfig))

if (jsConfig && "config" in jsConfig && isValidConfiguration(jsConfig.config)) {
return objectToDashConfig(jsConfig.config);
const config = jsConfig.config;
const result = {
version: config.version,
flow: config.flow,
intent: config.intent,
language: config.language,
"employee-ids": config.employee?.ids,
"employee-type": config.employee?.type,
"application-scroll": config.application?.scroll,
"application-time_zone_selection": config.application?.timeZoneSelection,
// "application-timetable_ui": config.application?.timetableUI,
"application-theme": config.application?.theme,
"customer-company": config.customer?.company,
"customer-customer_number": config.customer?.customerNumber,
"customer-email": config.customer?.email,
"customer-existing": config.customer?.existing,
"customer-external_id": config.customer?.externalId,
"customer-first_name": config.customer?.firstName,
"customer-id": config.customer?.id,
"customer-language": config.customer?.language,
"customer-last_name": config.customer?.lastName,
"customer-location-city": config.customer?.location?.city,
"customer-location-country_code": config.customer?.location?.countryCode,
"customer-location-geolocation-latitude": config.customer?.location?.geolocation?.latitude,
"customer-location-geolocation-longitude": config.customer?.location?.geolocation?.longitude,
"customer-location-postal_code": config.customer?.location?.postalCode,
"customer-location-time_zone": config.customer?.location?.timeZone,
"customer-location-state": config.customer?.location?.state,
"customer-location-street1": config.customer?.location?.street1,
"customer-location-street2": config.customer?.location?.street2,
"customer-phone_number": config.customer?.phoneNumber,
"customer-time_zone": config.customer?.timeZone,
"lead_segment-id": config.leadSegment?.id,
"lead_segment-type": config.leadSegment?.type,
"listing-id": config.listing?.id,
"listing-type": config.listing?.type,
"location-formatted_address": config.location?.formattedAddress,
meeting_types: config.meetingTypes,
"oauth-resource_code": config.oauth?.resourceCode,
"office-ids": config.office?.ids,
"office-type": config.office?.type,
search_countries: config.searchCountries,
"session-id": config.session?.id,
"session-source_tags": config.session?.sourceTags,
"session-status": config.session?.status,
"subject-ids": config.subject?.ids,
"subject-type": config.subject?.type,
"subject_group-ids": config.subjectGroup?.ids,
"subject_group-type": config.subjectGroup?.type,
};
return prune(result);
}
else {
return {};
return null;
}

@@ -32,34 +151,20 @@ }

logger.error("JS ERROR:", err);
return {};
return null;
}
},
};
function objectToDashConfig(inputObject, current, result = {}) {
for (let key in inputObject) {
let value = inputObject[key];
let newKey = current ? `${current}-${key}` : key;
if (Array.isArray(value)) {
result[newKey] = value;
function prune(obj) {
for (let key in obj) {
const value = obj[key];
if (value === null || value === undefined) {
delete obj[key];
}
else if (value && typeof value === "object") {
objectToDashConfig(value, newKey, result);
else if (typeof value === "object") {
prune(value);
if (Object.keys(value).length === 0)
delete obj[key];
}
else {
result[newKey] = value;
}
}
return result;
return obj;
}
function setValue(target, keys, value) {
let targetObj = target;
const [lastKey] = keys.splice(-1, 1);
for (const key of keys) {
if (!targetObj[key]) {
targetObj[key] = {};
}
targetObj = targetObj[key];
}
//@ts-expect-error
targetObj[lastKey] = value;
}
//# sourceMappingURL=js-parser.js.map
import type { Language } from "@pexip-engage-public/graphql";
export declare const DEPRECATED_TOKEN: {
readonly LOCK: "#";
readonly OFFICE: "L";
readonly START: "'";
};
export declare const TOKEN: {

@@ -8,0 +3,0 @@ readonly EMPLOYEE: "E";

@@ -1,6 +0,1 @@

export const DEPRECATED_TOKEN = {
LOCK: "#",
OFFICE: "L",
START: "'",
};
export const TOKEN = {

@@ -7,0 +2,0 @@ EMPLOYEE: "E",

import type { PluginConfiguration } from "./configuration/PluginConfiguration.current.js";
export declare function encodeURIParameters<T>(obj: T): string;
export declare function decodeURIParameters<T>(str: string): T;
export declare const pluginParams: {
export declare const pluginSearchParams: {
encode(config: PluginConfiguration): URLSearchParams;

@@ -6,0 +6,0 @@ decode(searchParams: URLSearchParams): PluginConfiguration | null;

@@ -16,3 +16,3 @@ export function encodeURIParameters(obj) {

const CONFIGURATION_SEARCH_PARAM_KEY = "configuration.pexip";
export const pluginParams = {
export const pluginSearchParams = {
encode(config) {

@@ -19,0 +19,0 @@ const searchParams = new URLSearchParams();

@@ -24,3 +24,3 @@ import type { InputType } from "@pexip-engage-public/graphql";

};
meetingType?: "video" | "phone" | "on_location" | "office" | "VIDEO" | "PHONE" | "ON_LOCATION" | "OFFICE";
meetingType?: "VIDEO" | "PHONE" | "ON_LOCATION" | "OFFICE";
employee?: {

@@ -27,0 +27,0 @@ id: string;

export const PEXIP_ENGAGE_PLUGIN_EVENT = "PexipEngagePluginEvent";
const DEPRECATED_SKEDIFY_PLUGIN_EVENT = "SkedifyPluginEvent";
export function dispatchEvent({ target, ...eventInitDict }) {
const pexipEngageEvent = new CustomEvent(PEXIP_ENGAGE_PLUGIN_EVENT, eventInitDict);
const skedifyEvent = new CustomEvent(DEPRECATED_SKEDIFY_PLUGIN_EVENT, eventInitDict);
target.dispatchEvent(skedifyEvent);
return target.dispatchEvent(pexipEngageEvent);
}
//# sourceMappingURL=dispatchEvent.js.map

@@ -6,16 +6,26 @@ import { PluginInstance as _PluginInstance } from "./PluginInstance.js";

export declare const PluginStatic: typeof _PluginInstance;
export interface PexipEngageWindow {
SkedifyPlugin?: typeof PluginStatic;
Skedify?: {
Plugin: typeof PluginStatic;
};
PexipEngage?: {
Plugin: typeof PluginStatic;
};
}
export declare function installOnGlobal(options?: {
shouldWarn: boolean;
}): void;
declare global {
interface Window extends PexipEngageWindow {
interface Window {
PexipEngage: IPexipEngage;
}
}
export declare function installGlobals(): void;
export type PexipEngagePluginArgs = ["init"] | ["get-instance"] | ["create", {
element: Element;
options?: unknown;
}];
export interface IPexipEngage {
(event: "init"): undefined;
(event: "get-instance"): Promise<PluginInstance>;
(event: "create", options: {
element: Element;
options?: unknown;
}): Promise<PluginInstance>;
q: [PexipEngagePluginArgs, (value: PluginInstance) => void, (reason?: unknown) => void][];
loaded?: boolean;
/** @deprecated Please use the PexipEngage function directly to interact with the plugin. */
Plugin?: typeof PluginStatic;
}
//# sourceMappingURL=index.d.ts.map

@@ -9,19 +9,19 @@ import { dispatchEvent } from "./dispatchEvent.js";

});
export function installGlobals() {
if (!window.Skedify?.Plugin) {
window.Skedify = {
...window.Skedify,
Plugin: PluginStatic,
};
}
if (!window.SkedifyPlugin) {
window.SkedifyPlugin = PluginStatic;
}
export function installOnGlobal(options = { shouldWarn: true }) {
if (!window.PexipEngage?.Plugin) {
window.PexipEngage = { ...window.PexipEngage, Plugin: PluginStatic };
if (options.shouldWarn && !window.PexipEngage) {
console.warn("Polyfilling window.PexipEngage, this should not happen. Please make sure you're using the correct initialization script.");
}
function PexipEngage() {
let pexip = window.PexipEngage;
pexip.q = pexip.q || [];
let args = arguments;
return new Promise((resolve, reject) => pexip.q.push([args, resolve, reject]));
}
PexipEngage.q = [];
window.PexipEngage = window.PexipEngage || PexipEngage;
window.PexipEngage.Plugin = PluginStatic;
dispatchEvent({
target: document,
detail: {
type: PluginStatic.EVENT_INIT,
},
detail: { type: PluginStatic.EVENT_INIT },
bubbles: true,

@@ -28,0 +28,0 @@ cancelable: true,

@@ -19,91 +19,40 @@ import { parsePluginConfiguration } from "../configuration-parser/index.js";

setCustomCSS: (css: string) => void;
/** Add an event listener */
addEventListener: (typeOrListener?: CustomEventPayload["type"] | PluginEventListener, cb?: PluginEventListener) => void;
/** The current intention */
get intention(): PluginIntent | undefined;
/** Add an event listener. Returns an unsubscribe function. */
addEventListener: (typeOrListener?: CustomEventPayload["type"] | PluginEventListener, cb?: PluginEventListener) => () => void;
get appointment(): {
id: string | undefined;
location: {
id: string | undefined;
};
meeting_type: "VIDEO" | "PHONE" | "ON_LOCATION" | "OFFICE" | undefined;
subject: {
id: string | undefined;
category_id: string | undefined;
};
meta: {
getAll: () => Record<string, unknown>;
get: (key: string, defaultValue: unknown) => unknown;
set: (key: string, value: unknown) => Record<string, unknown>;
remove: (key: string) => void;
};
};
get customer(): {
company?: string | null | undefined;
customerNumber?: string | null | undefined;
email?: string | null | undefined;
existing?: "on" | undefined;
externalId?: string | null | undefined;
firstName?: string | null | undefined;
id?: string | null | undefined;
language?: string | null | undefined;
lastName?: string | null | undefined;
location?: {
city?: string | null | undefined;
countryCode?: string | null | undefined;
geolocation?: {
latitude: number;
longitude: number;
intent: "schedule" | "reschedule" | "invite" | "edit" | "cancel" | "complete" | "reject" | undefined;
appointmentId: string | undefined;
callbackRequestId: string | undefined;
officeId: string | undefined;
meetingType: "VIDEO" | "PHONE" | "ON_LOCATION" | "OFFICE" | undefined;
subjectId: string | undefined;
subjectGroupId: string | undefined;
employeeId: string | undefined;
customer: {
company?: string | null | undefined;
customerNumber?: string | null | undefined;
email?: string | null | undefined;
existing?: "on" | undefined;
externalId?: string | null | undefined;
firstName?: string | null | undefined;
id?: string | null | undefined;
language?: string | null | undefined;
lastName?: string | null | undefined;
location?: {
city?: string | null | undefined;
countryCode?: string | null | undefined;
geolocation?: {
latitude: number;
longitude: number;
} | null | undefined;
postalCode?: string | null | undefined;
timeZone?: string | null | undefined;
state?: string | null | undefined;
street1?: string | null | undefined;
street2?: string | null | undefined;
} | null | undefined;
postalCode?: string | null | undefined;
phoneNumber?: string | null | undefined;
timeZone?: string | null | undefined;
state?: string | null | undefined;
street1?: string | null | undefined;
street2?: string | null | undefined;
} | null | undefined;
phoneNumber?: string | null | undefined;
timeZone?: string | null | undefined;
additionalCustomers?: string[] | undefined;
} | undefined;
static dispose: () => void | undefined;
static disposeAll: () => void;
static addEventListener: (typeOrListener?: "STEP_SHOWN" | "ERROR" | "MISCONFIGURED" | "APPOINTMENT_CREATED" | "APPOINTMENT_EDITED" | "INVITE_ACCEPTED" | "APPOINTMENT_CANCELLED" | "APPOINTMENT_COMPLETED" | "INIT" | "CREATION" | "LOADED" | PluginEventListener | undefined, cb?: PluginEventListener | undefined) => void | undefined;
static setCSSVariable(name: string, value: string): void | undefined;
static get customer(): {
company?: string | null | undefined;
customerNumber?: string | null | undefined;
email?: string | null | undefined;
existing?: "on" | undefined;
externalId?: string | null | undefined;
firstName?: string | null | undefined;
id?: string | null | undefined;
language?: string | null | undefined;
lastName?: string | null | undefined;
location?: {
city?: string | null | undefined;
countryCode?: string | null | undefined;
geolocation?: {
latitude: number;
longitude: number;
} | null | undefined;
postalCode?: string | null | undefined;
timeZone?: string | null | undefined;
state?: string | null | undefined;
street1?: string | null | undefined;
street2?: string | null | undefined;
} | null | undefined;
phoneNumber?: string | null | undefined;
timeZone?: string | null | undefined;
additionalCustomers?: string[] | undefined;
} | undefined;
static get appointment(): {
id: string | undefined;
location: {
id: string | undefined;
};
meeting_type: "VIDEO" | "PHONE" | "ON_LOCATION" | "OFFICE" | undefined;
subject: {
id: string | undefined;
category_id: string | undefined;
};
additionalCustomers?: string[] | undefined;
} | undefined;
meta: {

@@ -115,14 +64,6 @@ getAll: () => Record<string, unknown>;

};
} | undefined;
static get intention(): "schedule" | "reschedule" | "invite" | "edit" | "cancel" | "complete" | "reject" | undefined;
};
static openDebugger(): string | undefined;
static setEnterpriseUrl(url: string): void;
static getInstance: (num: unknown) => PluginInstance | undefined;
static awaitFirstInstance: () => Promise<PluginInstance>;
static get count(): number;
static readonly EVENT_APPOINTMENT_CANCELLED: PluginEventMessage["type"];
static readonly EVENT_APPOINTMENT_COMPLETED: PluginEventMessage["type"];
static readonly EVENT_APPOINTMENT_CREATED: PluginEventMessage["type"];
static readonly EVENT_INVITE_ACCEPTED: PluginEventMessage["type"];
static readonly EVENT_APPOINTMENT_EDITED: PluginEventMessage["type"];
static readonly EVENT_CREATION: InstanceEvent["type"];

@@ -133,21 +74,10 @@ static readonly EVENT_ERROR: PluginEventMessage["type"];

static readonly EVENT_MISCONFIGURED: PluginEventMessage["type"];
static readonly EVENT_STEP_SHOWN: StepShownMessage["type"];
static readonly INTENT_CANCEL: PluginIntent;
static readonly INTENT_EDIT: PluginIntent;
static readonly INTENT_INVITE: PluginIntent;
static readonly INTENT_RESCHEDULE: PluginIntent;
static readonly INTENT_COMPLETE: PluginIntent;
static readonly INTENT_SCHEDULE: PluginIntent;
static readonly INTENT_REJECT: PluginIntent;
readonly EVENT_APPOINTMENT_CANCELLED: "ERROR" | "MISCONFIGURED" | "APPOINTMENT_CREATED" | "APPOINTMENT_EDITED" | "INVITE_ACCEPTED" | "APPOINTMENT_CANCELLED" | "APPOINTMENT_COMPLETED";
readonly EVENT_APPOINTMENT_COMPLETED: "ERROR" | "MISCONFIGURED" | "APPOINTMENT_CREATED" | "APPOINTMENT_EDITED" | "INVITE_ACCEPTED" | "APPOINTMENT_CANCELLED" | "APPOINTMENT_COMPLETED";
readonly EVENT_APPOINTMENT_CREATED: "ERROR" | "MISCONFIGURED" | "APPOINTMENT_CREATED" | "APPOINTMENT_EDITED" | "INVITE_ACCEPTED" | "APPOINTMENT_CANCELLED" | "APPOINTMENT_COMPLETED";
readonly EVENT_INVITE_ACCEPTED: "ERROR" | "MISCONFIGURED" | "APPOINTMENT_CREATED" | "APPOINTMENT_EDITED" | "INVITE_ACCEPTED" | "APPOINTMENT_CANCELLED" | "APPOINTMENT_COMPLETED";
readonly EVENT_APPOINTMENT_EDITED: "ERROR" | "MISCONFIGURED" | "APPOINTMENT_CREATED" | "APPOINTMENT_EDITED" | "INVITE_ACCEPTED" | "APPOINTMENT_CANCELLED" | "APPOINTMENT_COMPLETED";
readonly EVENT_CREATION: "CREATION" | "LOADED";
readonly EVENT_ERROR: "ERROR" | "MISCONFIGURED" | "APPOINTMENT_CREATED" | "APPOINTMENT_EDITED" | "INVITE_ACCEPTED" | "APPOINTMENT_CANCELLED" | "APPOINTMENT_COMPLETED";
readonly EVENT_INIT: "INIT";
readonly EVENT_LOADED: "CREATION" | "LOADED";
readonly EVENT_MISCONFIGURED: "ERROR" | "MISCONFIGURED" | "APPOINTMENT_CREATED" | "APPOINTMENT_EDITED" | "INVITE_ACCEPTED" | "APPOINTMENT_CANCELLED" | "APPOINTMENT_COMPLETED";
readonly EVENT_STEP_SHOWN: "STEP_SHOWN";
readonly EVENT_APPOINTMENT_CANCELLED: PluginEventMessage["type"];
readonly EVENT_APPOINTMENT_COMPLETED: PluginEventMessage["type"];
readonly EVENT_APPOINTMENT_CREATED: PluginEventMessage["type"];
readonly EVENT_INVITE_ACCEPTED: PluginEventMessage["type"];
readonly EVENT_APPOINTMENT_EDITED: PluginEventMessage["type"];
readonly EVENT_STEP_SHOWN: StepShownMessage["type"];
readonly INTENT_CANCEL: PluginIntent;

@@ -154,0 +84,0 @@ readonly INTENT_EDIT: PluginIntent;

import resizer, {} from "iframe-resizer/js/iframeResizer.js";
import { parsePluginConfiguration } from "../configuration-parser/index.js";
import { encodeURIParameters, pluginParams } from "../encoding.js";
import { encodeURIParameters, pluginSearchParams } from "../encoding.js";
import {} from "../events/index.js";

@@ -33,3 +33,3 @@ import { logger } from "../logger.js";

const existingInstance = PluginInstance.#instances.find((i) => i.#target === element);
this.#fallbackHTML = existingInstance ? "" : element.innerHTML;
this.#fallbackHTML = existingInstance ? existingInstance.#fallbackHTML : element.innerHTML;
existingInstance?.dispose();

@@ -39,3 +39,3 @@ const container = new PexipEngagePluginFrame();

element.appendChild(container);
const searchParams = pluginParams.encode(config.config);
const searchParams = pluginSearchParams.encode(config.config);
const src = `${PluginInstance.#url}/plugin?${searchParams}`;

@@ -72,2 +72,14 @@ const iframe = container.createPexipPlugin(src);

});
if (window.location.hash.startsWith("#skedify")) {
this.dispose();
element.innerHTML = "";
const url = new URL(PluginInstance.#url);
const enterprise = url.pathname.split("/")[1];
const hashParam = new URLSearchParams({ hash: window.location.hash });
enterprise && hashParam.append("enterprise", enterprise);
const errorElement = document.createElement("div");
errorElement.innerHTML = `This hash format is not supported anymore.
Please use the <a href="${url.origin}/docs/migration-assistant?${hashParam}" style="text-decoration: underline;">Migration assistant to get your new hash.</a>`;
element.appendChild(errorElement);
}
}

@@ -180,3 +192,3 @@ #handleMessage = async ({ message }) => {

#listeners = new Set();
/** Add an event listener */
/** Add an event listener. Returns an unsubscribe function. */
addEventListener = (typeOrListener, cb) => {

@@ -186,3 +198,3 @@ const type = isFunction(typeOrListener) ? undefined : typeOrListener;

if (!isFunction(listener))
return;
return () => { };
function internalListener(event) {

@@ -196,18 +208,18 @@ if (!isCustomEvent(event))

this.#target.addEventListener(PEXIP_ENGAGE_PLUGIN_EVENT, internalListener);
return () => {
this.#target.removeEventListener(PEXIP_ENGAGE_PLUGIN_EVENT, internalListener);
this.#listeners.delete(internalListener);
};
};
/** The current intention */
get intention() {
return this.#state?.config.intent;
}
get appointment() {
return {
id: this.#state?.appointmentId,
location: {
id: this.#state?.officeId,
},
meeting_type: this.#state?.meetingType,
subject: {
id: this.#state?.subjectId,
category_id: this.#state?.subjectCategoryId,
},
intent: this.#state?.config.intent,
appointmentId: this.#state?.appointmentId,
callbackRequestId: this.#state?.callbackRequestId,
officeId: this.#state?.officeId,
meetingType: this.#state?.meetingType,
subjectId: this.#state?.subjectId,
subjectGroupId: this.#state?.subjectCategoryId,
employeeId: this.#state?.employeeId,
customer: this.#state?.customer,
meta: {

@@ -230,29 +242,4 @@ getAll: () => {

}
get customer() {
// fallback to config value when customer state itself it still undefined?
return this.#state?.customer;
}
static dispose = () => {
return PluginInstance.getInstance(0)?.dispose();
};
static disposeAll = () => {
PluginInstance.#instances.forEach((instance) => instance.dispose());
};
static addEventListener = (...args) => {
return PluginInstance.getInstance(0)?.addEventListener(...args);
};
static setCSSVariable(name, value) {
return PluginInstance.getInstance(0)?.setCSSVariable(name, value);
}
static get customer() {
return PluginInstance.getInstance(0)?.customer;
}
static get appointment() {
return PluginInstance.getInstance(0)?.appointment;
}
static get intention() {
return PluginInstance.getInstance(0)?.intention;
}
static openDebugger() {
return PluginInstance.getInstance(0)?.openDebugger();
return PluginInstance.#getInstance(0)?.openDebugger();
}

@@ -268,3 +255,3 @@ static #url = "";

static #instances = [];
static getInstance = (num) => {
static #getInstance = (num) => {
if (!isPositiveNumber(num))

@@ -275,3 +262,3 @@ return undefined;

static awaitFirstInstance = async () => {
const instance = PluginInstance.getInstance(0);
const instance = PluginInstance.#getInstance(0);
if (instance)

@@ -299,10 +286,2 @@ return instance;

};
static get count() {
return PluginInstance.#instances.length;
}
static EVENT_APPOINTMENT_CANCELLED = "APPOINTMENT_CANCELLED";
static EVENT_APPOINTMENT_COMPLETED = "APPOINTMENT_COMPLETED";
static EVENT_APPOINTMENT_CREATED = "APPOINTMENT_CREATED";
static EVENT_INVITE_ACCEPTED = "INVITE_ACCEPTED";
static EVENT_APPOINTMENT_EDITED = "APPOINTMENT_EDITED";
static EVENT_CREATION = "CREATION";

@@ -313,28 +292,17 @@ static EVENT_ERROR = "ERROR";

static EVENT_MISCONFIGURED = "MISCONFIGURED";
static EVENT_STEP_SHOWN = "STEP_SHOWN";
static INTENT_CANCEL = "cancel";
static INTENT_EDIT = "edit";
static INTENT_INVITE = "invite";
static INTENT_RESCHEDULE = "reschedule";
static INTENT_COMPLETE = "complete";
static INTENT_SCHEDULE = "schedule";
static INTENT_REJECT = "reject";
EVENT_APPOINTMENT_CANCELLED = PluginInstance.EVENT_APPOINTMENT_CANCELLED;
EVENT_APPOINTMENT_COMPLETED = PluginInstance.EVENT_APPOINTMENT_COMPLETED;
EVENT_APPOINTMENT_CREATED = PluginInstance.EVENT_APPOINTMENT_CREATED;
EVENT_INVITE_ACCEPTED = PluginInstance.EVENT_INVITE_ACCEPTED;
EVENT_APPOINTMENT_EDITED = PluginInstance.EVENT_APPOINTMENT_EDITED;
EVENT_CREATION = PluginInstance.EVENT_CREATION;
EVENT_ERROR = PluginInstance.EVENT_ERROR;
EVENT_INIT = PluginInstance.EVENT_INIT;
EVENT_LOADED = PluginInstance.EVENT_LOADED;
EVENT_MISCONFIGURED = PluginInstance.EVENT_MISCONFIGURED;
EVENT_STEP_SHOWN = PluginInstance.EVENT_STEP_SHOWN;
INTENT_CANCEL = PluginInstance.INTENT_CANCEL;
INTENT_EDIT = PluginInstance.INTENT_EDIT;
INTENT_INVITE = PluginInstance.INTENT_INVITE;
INTENT_RESCHEDULE = PluginInstance.INTENT_RESCHEDULE;
INTENT_COMPLETE = PluginInstance.INTENT_COMPLETE;
INTENT_SCHEDULE = PluginInstance.INTENT_SCHEDULE;
INTENT_REJECT = PluginInstance.INTENT_REJECT;
EVENT_APPOINTMENT_CANCELLED = "APPOINTMENT_CANCELLED";
EVENT_APPOINTMENT_COMPLETED = "APPOINTMENT_COMPLETED";
EVENT_APPOINTMENT_CREATED = "APPOINTMENT_CREATED";
EVENT_INVITE_ACCEPTED = "INVITE_ACCEPTED";
EVENT_APPOINTMENT_EDITED = "APPOINTMENT_EDITED";
EVENT_STEP_SHOWN = "STEP_SHOWN";
INTENT_CANCEL = "cancel";
INTENT_EDIT = "edit";
INTENT_INVITE = "invite";
INTENT_RESCHEDULE = "reschedule";
INTENT_COMPLETE = "complete";
INTENT_SCHEDULE = "schedule";
INTENT_REJECT = "reject";
}

@@ -341,0 +309,0 @@ function isPositiveNumber(num) {

{
"name": "@pexip-engage-public/plugin",
"version": "1.0.16",
"version": "1.0.17",
"homepage": "https://github.com/skedify/frontend-mono/tree/develop/apps/plugin-remix/packages/plugin-public#readme",

@@ -24,2 +24,3 @@ "bugs": {

"./configuration-parser": "./dist/configuration-parser/index.js",
"./configuration-parser/migrate": "./dist/configuration-parser/migrate-legacy-configuration.js",
"./events": "./dist/events/index.js",

@@ -26,0 +27,0 @@ "./instance": "./dist/instance/index.js",

import type { PluginConfigurationV0 } from "../configuration/PluginConfiguration.v0.ts";
import { DEPRECATED_TOKEN, TOKEN } from "../constants.js";
import { TOKEN } from "../constants.js";

@@ -7,2 +7,8 @@ import type { LegacyPluginHashConfiguration } from "./LegacyParser.js";

const DEPRECATED_TOKEN = {
LOCK: "#",
OFFICE: "L",
START: "'",
} as const;
export function legacyHashToPluginInputConfiguration(

@@ -9,0 +15,0 @@ config: LegacyPluginHashConfiguration,

@@ -9,4 +9,4 @@ import type { PluginConfiguration } from "../configuration/PluginConfiguration.current.js";

},
decode(hash?: string | null): PluginConfiguration {
if (!hash || !hash.startsWith(HASH_PREFIX)) return {};
decode(hash?: string | null): PluginConfiguration | null {
if (!hash || !hash.startsWith(HASH_PREFIX)) return null;

@@ -13,0 +13,0 @@ const value = hash.slice(HASH_PREFIX.length);

@@ -16,5 +16,5 @@ import type { PluginConfiguration } from "../configuration/PluginConfiguration.current.js";

},
decode(element?: HTMLElement | null): PluginConfiguration {
decode(element?: HTMLElement | null): PluginConfiguration | null {
try {
if (!element) return {};
if (!element) return null;
const attributes = element.getAttributeNames().filter((name) => name.startsWith(ATTR_PREFIX));

@@ -34,5 +34,5 @@ const object: PluginConfiguration = {};

return {};
return null;
}
},
};

@@ -1,4 +0,2 @@

import { migrateConfiguration } from "../configuration/migration.js";
import type { PluginConfiguration } from "../configuration/PluginConfiguration.current.js";
import { parsePluginConfigurationV1 } from "../configuration-parser-legacy/Parser.js";
import { logger } from "../logger.js";

@@ -8,4 +6,5 @@

import { htmlParser } from "./html-parser.js";
import { type JSConfig, jsParser } from "./js-parser.js";
import { jsParser } from "./js-parser.js";
// import { migrateLegacyConfiguration } from './migrate-legacy-configuration.js';
export const CONFIG_VERSION: Exclude<PluginConfiguration["version"], undefined> = "1.0.0";

@@ -17,3 +16,3 @@ export const parser = { js: jsParser, hash: hashParser, html: htmlParser };

interface ConfigurationProps {
export interface ConfigurationProps {
element?: HTMLElement;

@@ -32,2 +31,10 @@ hash?: string;

if (!version) {
logger.warn(
`Failed to detect config version, assuming latest version.
Please add a version to your config to avoid breaking it in the future.
Current version: ${CONFIG_VERSION}`,
);
}
// only one version for now, can just return it.

@@ -38,14 +45,5 @@ if (version === CONFIG_VERSION) {

const v1Config = parsePluginConfigurationV1({
element: args.element,
hash: args.hash,
href: args.browser.href,
browserTimeZone: args.browser.timeZone,
options: args.options,
});
return result;
const migrated = createMigration(v1Config);
// migrated.
return migrated;
// return migrateLegacyConfiguration(args);
}

@@ -68,73 +66,1 @@

}
function createMigration(result: ReturnType<typeof parsePluginConfigurationV1>): {
migrationInstructions: {
dom: string | null;
hash: string | null;
js: JSConfig | null;
};
config: PluginConfiguration;
domConfig: PluginConfiguration | null;
hashConfig: PluginConfiguration | null;
jsConfig: PluginConfiguration | null;
} {
const data = {
config: migrateConfiguration(result.config) ?? { version: "1.0.0" },
domConfig: migrateConfiguration(result.domConfig),
hashConfig: migrateConfiguration(result.hashConfig),
jsConfig: migrateConfiguration(result.jsConfig),
};
const migrationInstructions = {
dom: data.domConfig
? `<div class="pexip-engage-plugin"\n ${parser.html
.encode(data.domConfig)
.join("\n ")}\n></div>`
: null,
hash: data.hashConfig ? parser.hash.encode(data.hashConfig) : null,
js: data.jsConfig ? parser.js.encode(data.jsConfig) : null,
};
logDeprecationWarning(migrationInstructions);
return {
...data,
migrationInstructions,
};
}
function logDeprecationWarning({
dom,
hash,
js,
}: {
dom: string | null;
js: JSConfig | null;
hash: string | null;
}) {
if (dom || hash || js) {
logger.group("DEPRECATED CONFIGURATION");
if (dom) {
try {
logger.warn(
"Detected unsupported DOM config. Please update your DOM element to:\n",
dom.replaceAll("\n ", ""),
);
} catch (err) {}
}
if (hash) {
logger.warn("Detected deprecated hash config. Please update your hash to:\n%o", hash);
}
if (js) {
logger.warn(
"Detected deprecated JS config. Please update your JS to:\n%o",
`PexipEngage.Plugin(element, ${JSON.stringify(js)})`,
);
}
logger.groupEnd();
}
}

@@ -0,22 +1,92 @@

import type { MeetingType } from "@pexip-engage-public/graphql";
import type { PluginConfiguration } from "../configuration/PluginConfiguration.current.js";
import { isValidConfiguration } from "../configuration-parser-legacy/utils.js";
import { logger } from "../logger.js";
function isValidConfiguration(configuration: unknown): configuration is object {
return typeof configuration === "object" && configuration !== null;
}
export const jsParser = {
encode(config: PluginConfiguration): JSConfig {
//@ts-ignore
const result: JSConfig["config"] = {};
const result: JSConfig["config"] = {
version: config.version,
flow: config.flow,
intent: config.intent as any,
language: config.language as any,
employee: {
ids: config["employee-ids"],
type: config["employee-type"] as any,
},
application: {
scroll: config["application-scroll"] as any,
timeZoneSelection: config["application-time_zone_selection"] as any,
// timetable_ui: config["application-timetable_ui"],
theme: config["application-theme"],
},
customer: {
company: config["customer-company"],
customerNumber: config["customer-customer_number"],
email: config["customer-email"],
existing: config["customer-existing"],
externalId: config["customer-external_id"],
firstName: config["customer-first_name"],
id: config["customer-id"],
language: config["customer-language"],
lastName: config["customer-last_name"],
location: {
city: config["customer-location-city"],
countryCode: config["customer-location-country_code"],
geolocation: {
latitude: config["customer-location-geolocation-latitude"] as any,
longitude: config["customer-location-geolocation-longitude"] as any,
},
postalCode: config["customer-location-postal_code"],
timeZone: config["customer-location-time_zone"],
state: config["customer-location-state"],
street1: config["customer-location-street1"],
street2: config["customer-location-street2"],
},
phoneNumber: config["customer-phone_number"],
timeZone: config["customer-time_zone"],
},
leadSegment: {
id: config["lead_segment-id"],
type: config["lead_segment-type"] as any,
},
listing: {
id: config["listing-id"],
type: config["listing-type"] as any,
},
location: {
formattedAddress: config["location-formatted_address"],
},
meetingTypes: config["meeting_types"] as any,
oauth: {
resourceCode: config["oauth-resource_code"],
},
office: {
ids: config["office-ids"],
type: config["office-type"] as any,
},
searchCountries: config["search_countries"],
session: {
id: config["session-id"],
sourceTags: config["session-source_tags"],
status: config["session-status"] as any,
},
subject: {
ids: config["subject-ids"],
type: config["subject-type"] as any,
},
subjectGroup: {
ids: config["subject_group-ids"],
type: config["subject_group-type"] as any,
},
};
for (let key in config) {
const value = (config as any)[key];
const keys = key.split("-");
//@ts-ignore
setValue(result, keys, value);
}
return { config: result };
return { config: prune(result) };
},
decode(jsConfig?: unknown): PluginConfiguration {
if (!jsConfig) return {};
decode(jsConfig?: unknown): PluginConfiguration | null {
if (!jsConfig) return null;

@@ -28,5 +98,59 @@ try {

if (jsConfig && "config" in jsConfig && isValidConfiguration(jsConfig.config)) {
return objectToDashConfig(jsConfig.config) as unknown as PluginConfiguration;
const config = jsConfig.config as NonNullable<JSConfig["config"]>;
const result: PluginConfiguration = {
version: config.version,
flow: config.flow,
intent: config.intent,
language: config.language,
"employee-ids": config.employee?.ids,
"employee-type": config.employee?.type,
"application-scroll": config.application?.scroll,
"application-time_zone_selection": config.application?.timeZoneSelection,
// "application-timetable_ui": config.application?.timetableUI,
"application-theme": config.application?.theme,
"customer-company": config.customer?.company,
"customer-customer_number": config.customer?.customerNumber,
"customer-email": config.customer?.email,
"customer-existing": config.customer?.existing,
"customer-external_id": config.customer?.externalId,
"customer-first_name": config.customer?.firstName,
"customer-id": config.customer?.id,
"customer-language": config.customer?.language,
"customer-last_name": config.customer?.lastName,
"customer-location-city": config.customer?.location?.city,
"customer-location-country_code": config.customer?.location?.countryCode,
"customer-location-geolocation-latitude":
config.customer?.location?.geolocation?.latitude,
"customer-location-geolocation-longitude":
config.customer?.location?.geolocation?.longitude,
"customer-location-postal_code": config.customer?.location?.postalCode,
"customer-location-time_zone": config.customer?.location?.timeZone,
"customer-location-state": config.customer?.location?.state,
"customer-location-street1": config.customer?.location?.street1,
"customer-location-street2": config.customer?.location?.street2,
"customer-phone_number": config.customer?.phoneNumber,
"customer-time_zone": config.customer?.timeZone,
"lead_segment-id": config.leadSegment?.id,
"lead_segment-type": config.leadSegment?.type,
"listing-id": config.listing?.id,
"listing-type": config.listing?.type,
"location-formatted_address": config.location?.formattedAddress,
meeting_types: config.meetingTypes,
"oauth-resource_code": config.oauth?.resourceCode,
"office-ids": config.office?.ids,
"office-type": config.office?.type,
search_countries: config.searchCountries,
"session-id": config.session?.id,
"session-source_tags": config.session?.sourceTags,
"session-status": config.session?.status,
"subject-ids": config.subject?.ids,
"subject-type": config.subject?.type,
"subject_group-ids": config.subjectGroup?.ids,
"subject_group-type": config.subjectGroup?.type,
};
return prune(result);
} else {
return {};
return null;
}

@@ -37,3 +161,3 @@ } catch (err) {

return {};
return null;
}

@@ -43,21 +167,15 @@ },

function objectToDashConfig(
inputObject: Record<any, any>,
current?: string,
result: Record<any, any> = {},
) {
for (let key in inputObject) {
let value = inputObject[key];
let newKey = current ? `${current}-${key}` : key;
function prune<T>(obj: T): T {
for (let key in obj) {
const value = obj[key];
if (Array.isArray(value)) {
result[newKey] = value;
} else if (value && typeof value === "object") {
objectToDashConfig(value, newKey, result);
} else {
result[newKey] = value;
if (value === null || value === undefined) {
delete obj[key];
} else if (typeof value === "object") {
prune(value);
if (Object.keys(value).length === 0) delete obj[key];
}
}
return result;
return obj;
}

@@ -71,13 +189,68 @@

config?: {
version: "1.0.0";
version?: "1.0.0";
/**
* A sequence of characters that indicates the order of steps to be shown. The following characters are supported:
* - `S`: Subject. The step that allows the customer to pick a subject.
* - `O`: Office. The step that allows the customer to pick an office.
* - `M`: Meeting type. The step that allows the customer to pick a meeting type.
* - `E`: Employee. The step that allows the customer to pick an employee.
* - `T`: Timetable. The step that allows the customer to suggest one or more time slots for their appointment.
* - `Q`: Questions. The step that allows the customer to answer any addition questions specific to the chosen subject.
* - `C`: Customer. The step that allows the customer to provide his/her contact information.
* - `*`: [locked]. Indicate that the step is locked and can not be edited. This means that a value must be provided.
*
* Currently, the following constraints must always be met:
* - The following characters must always be present: `T`, `Q`, `C`.
* - No other characters than those listed above may be used.
* - When `S` is not present, it is mandatory that an external mechanism will provide the `config.subject.id` + `config.subject.type` configuration option instead.
* - When `O` is not present, it is mandatory that an external mechanism will provide the `config.office.id` + `config.office.type`configuration option instead.
* - If `S` or `O` are present, they can never occur at a position after `T`.
* - If `S` is present, it can never occur at a position after `Q`.
*
* By default the value `SMOTQC` will be assumed. This will be the case if no value was provided or if the provided value fails these constraints.
* @default "SMOTQC"
*/
flow?: StringConfig;
intent?: StringConfig;
language?: StringConfig;
intent?: "schedule" | "reschedule" | "invite" | "edit" | "cancel" | "complete" | null;
/**
* An RFC 5646-compatible language tag.
*
* This will cause the plugin to be displayed in the specified language, unless the language is unavailable.
*
* In that case, a best-effort fallback will be performed with increasingly more generic variants of the specified language or finally 'en' if no variants of the specified language are available.
*/
language?:
| "da"
| "de"
| "el"
| "en"
| "en-GB"
| "en-US"
| "es"
| "fr"
| "no"
| "nl"
| "pl"
| null;
employee?: {
ids?: StringArrayConfig;
type?: StringConfig;
type?: "id" | "externalId" | null;
};
application?: {
/**
* Used to set scroll top value when navigating steps
* @default -90
*/
scroll?: "disabled" | number | null;
timezoneSelection?: StringConfig;
/**
* Used to enable timezone selection when the meeting type is video or phone.
* @default "enable"
*/
timeZoneSelection?: "enable" | "disable" | null;
// /**
// * Used to decide wether to show month of week timetable view
// * @experimental
// * @default "week"
// */
// timetableUI?: "week" | "month" | null;
theme?: string | null;

@@ -88,38 +261,40 @@ };

company?: StringConfig;
customer_number?: StringConfig;
date_of_birth?: StringConfig;
customerNumber?: StringConfig;
email?: StringConfig;
external_id?: StringConfig;
first_name?: StringConfig;
gender?: StringConfig;
existing?: BooleanConfig;
externalId?: StringConfig;
firstName?: StringConfig;
id?: StringConfig;
is_existing?: BooleanConfig;
language?: StringConfig;
last_name?: StringConfig;
lastName?: StringConfig;
location?: {
city?: StringConfig;
country?: StringConfig;
geolocation?: StringConfig;
postal_code?: StringConfig;
countryCode?: StringConfig;
geolocation?: {
latitude: StringConfig;
longitude: StringConfig;
};
postalCode?: StringConfig;
timeZone?: StringConfig;
state?: StringConfig;
street_1?: StringConfig;
street1?: StringConfig;
street2?: StringConfig;
};
phone_number?: StringConfig;
preferred_contact_id?: StringConfig;
preferred_office_id?: StringConfig;
timezone?: StringConfig;
phoneNumber?: StringConfig;
timeZone?: StringConfig;
};
leadSegment?: {
id?: StringConfig;
type?: StringConfig;
type?: "id" | "code" | null;
};
listing?: {
id?: StringConfig;
type?: StringConfig;
type?: "id" | "externalId" | null;
};
location?: {
initialSearch?: StringConfig;
formattedAddress?: StringConfig;
};
meetingTypes?: StringArrayConfig;
meetingTypes?: MeetingType[] | MeetingType | null;
oauth?: {
/** A resource_code of an existing appointment. This will allow you to complete or change an appointment. This parameter is mostly used by redirects from external sources (e.g. e-mails) and should usually not be provided manually. */
resourceCode?: StringConfig;

@@ -129,33 +304,26 @@ };

ids?: StringArrayConfig;
type?: StringConfig;
type?: "id" | "externalId" | null;
};
/**
* The country that should be used for searching locations. Should be a two letter ISO 3166-1 country code.
*
* You can provide a maximum of 5 comma-separated countries.
*/
searchCountries?: StringArrayConfig;
session?: {
id?: StringConfig;
/** Pass source tags seperated with a semi-colon. */
sourceTags?: StringArrayConfig;
status?: StringConfig;
/** Used to enable/disable tracking of events in the plugin for insights about conversion. @default "default" */
status?: "default" | "disabled" | null;
};
subject?: {
ids?: StringArrayConfig;
type?: StringConfig;
type?: "id" | "externalId" | null;
};
timetable?: {
startDate?: StringConfig;
subjectGroup?: {
ids?: StringArrayConfig;
type?: "id" | "externalId" | null;
};
};
}
function setValue(target: Record<string, any>, keys: string[], value: unknown) {
let targetObj = target;
const [lastKey] = keys.splice(-1, 1);
for (const key of keys) {
if (!targetObj[key]) {
targetObj[key] = {};
}
targetObj = targetObj[key];
}
//@ts-expect-error
targetObj[lastKey] = value;
}
import type { Language } from "@pexip-engage-public/graphql";
export const DEPRECATED_TOKEN = {
LOCK: "#",
OFFICE: "L",
START: "'",
} as const;
export const TOKEN = {

@@ -10,0 +4,0 @@ EMPLOYEE: "E",

@@ -29,3 +29,3 @@ import type { PluginConfiguration } from "./configuration/PluginConfiguration.current.js";

export const pluginParams = {
export const pluginSearchParams = {
encode(config: PluginConfiguration): URLSearchParams {

@@ -32,0 +32,0 @@ const searchParams = new URLSearchParams();

@@ -29,11 +29,3 @@ import type { InputType } from "@pexip-engage-public/graphql";

office?: { id: string; title?: string };
meetingType?:
| "video"
| "phone"
| "on_location"
| "office"
| "VIDEO"
| "PHONE"
| "ON_LOCATION"
| "OFFICE";
meetingType?: "VIDEO" | "PHONE" | "ON_LOCATION" | "OFFICE";
employee?: { id: string; firstName?: string | null; lastName?: string | null };

@@ -40,0 +32,0 @@ };

@@ -8,4 +8,2 @@ import type { PublicIFrameMessage } from "../events/index.js";

const DEPRECATED_SKEDIFY_PLUGIN_EVENT = "SkedifyPluginEvent";
interface DispatchEventOptions<T> extends CustomEventInit<T> {

@@ -37,7 +35,4 @@ target: Element | Document | DocumentFragment;

const pexipEngageEvent = new CustomEvent(PEXIP_ENGAGE_PLUGIN_EVENT, eventInitDict);
const skedifyEvent = new CustomEvent(DEPRECATED_SKEDIFY_PLUGIN_EVENT, eventInitDict);
target.dispatchEvent(skedifyEvent);
return target.dispatchEvent(pexipEngageEvent);
}

@@ -16,36 +16,27 @@ import { dispatchEvent } from "./dispatchEvent.js";

export interface PexipEngageWindow {
SkedifyPlugin?: typeof PluginStatic;
Skedify?: {
Plugin: typeof PluginStatic;
};
PexipEngage?: {
Plugin: typeof PluginStatic;
};
}
export function installOnGlobal(options: { shouldWarn: boolean } = { shouldWarn: true }) {
if (!window.PexipEngage?.Plugin) {
if (options.shouldWarn && !window.PexipEngage) {
console.warn(
"Polyfilling window.PexipEngage, this should not happen. Please make sure you're using the correct initialization script.",
);
}
declare global {
interface Window extends PexipEngageWindow {}
}
function PexipEngage() {
let pexip = window.PexipEngage;
pexip.q = pexip.q || [];
let args: PexipEngagePluginArgs = arguments as any;
export function installGlobals() {
if (!window.Skedify?.Plugin) {
window.Skedify = {
...window.Skedify,
Plugin: PluginStatic,
};
}
return new Promise<PluginInstance>((resolve, reject) =>
pexip.q.push([args, resolve, reject]),
);
}
PexipEngage.q = [] as any[];
if (!window.SkedifyPlugin) {
window.SkedifyPlugin = PluginStatic;
}
window.PexipEngage = window.PexipEngage || PexipEngage;
window.PexipEngage.Plugin = PluginStatic;
if (!window.PexipEngage?.Plugin) {
window.PexipEngage = { ...window.PexipEngage, Plugin: PluginStatic };
dispatchEvent({
target: document,
detail: {
type: PluginStatic.EVENT_INIT,
},
detail: { type: PluginStatic.EVENT_INIT },
bubbles: true,

@@ -56,1 +47,22 @@ cancelable: true,

}
declare global {
interface Window {
PexipEngage: IPexipEngage;
}
}
export type PexipEngagePluginArgs =
| ["init"]
| ["get-instance"]
| ["create", { element: Element; options?: unknown }];
export interface IPexipEngage {
(event: "init"): undefined;
(event: "get-instance"): Promise<PluginInstance>;
(event: "create", options: { element: Element; options?: unknown }): Promise<PluginInstance>;
q: [PexipEngagePluginArgs, (value: PluginInstance) => void, (reason?: unknown) => void][];
loaded?: boolean;
/** @deprecated Please use the PexipEngage function directly to interact with the plugin. */
Plugin?: typeof PluginStatic;
}

@@ -7,3 +7,3 @@ import resizer, {

import { parsePluginConfiguration } from "../configuration-parser/index.js";
import { encodeURIParameters, pluginParams } from "../encoding.js";
import { encodeURIParameters, pluginSearchParams } from "../encoding.js";
import {

@@ -39,3 +39,2 @@ type PluginEventMessage,

#fallbackTimeoutId: number | null = null;
readonly #fallbackHTML: string;

@@ -61,3 +60,3 @@ readonly #config: ReturnType<typeof parsePluginConfiguration>;

const existingInstance = PluginInstance.#instances.find((i) => i.#target === element);
this.#fallbackHTML = existingInstance ? "" : element.innerHTML;
this.#fallbackHTML = existingInstance ? existingInstance.#fallbackHTML : element.innerHTML;
existingInstance?.dispose();

@@ -69,3 +68,3 @@

const searchParams = pluginParams.encode(config.config);
const searchParams = pluginSearchParams.encode(config.config);
const src = `${PluginInstance.#url}/plugin?${searchParams}`;

@@ -107,2 +106,17 @@

});
if (window.location.hash.startsWith("#skedify")) {
this.dispose();
element.innerHTML = "";
const url = new URL(PluginInstance.#url);
const enterprise = url.pathname.split("/")[1];
const hashParam = new URLSearchParams({ hash: window.location.hash });
enterprise && hashParam.append("enterprise", enterprise);
const errorElement = document.createElement("div");
errorElement.innerHTML = `This hash format is not supported anymore.
Please use the <a href="${url.origin}/docs/migration-assistant?${hashParam}" style="text-decoration: underline;">Migration assistant to get your new hash.</a>`;
element.appendChild(errorElement);
}
}

@@ -247,3 +261,3 @@

#listeners = new Set<EventListener>();
/** Add an event listener */
/** Add an event listener. Returns an unsubscribe function. */
addEventListener = (

@@ -256,3 +270,3 @@ typeOrListener?: CustomEventPayload["type"] | PluginEventListener,

if (!isFunction(listener)) return;
if (!isFunction(listener)) return () => {};

@@ -267,20 +281,20 @@ function internalListener(event: Event) {

this.#target.addEventListener(PEXIP_ENGAGE_PLUGIN_EVENT, internalListener);
return () => {
this.#target.removeEventListener(PEXIP_ENGAGE_PLUGIN_EVENT, internalListener);
this.#listeners.delete(internalListener);
};
};
/** The current intention */
get intention(): PluginIntent | undefined {
return this.#state?.config.intent;
}
get appointment() {
return {
id: this.#state?.appointmentId,
location: {
id: this.#state?.officeId,
},
meeting_type: this.#state?.meetingType,
subject: {
id: this.#state?.subjectId,
category_id: this.#state?.subjectCategoryId,
},
intent: this.#state?.config.intent,
appointmentId: this.#state?.appointmentId,
callbackRequestId: this.#state?.callbackRequestId,
officeId: this.#state?.officeId,
meetingType: this.#state?.meetingType,
subjectId: this.#state?.subjectId,
subjectGroupId: this.#state?.subjectCategoryId,
employeeId: this.#state?.employeeId,
customer: this.#state?.customer,
meta: {

@@ -304,40 +318,4 @@ getAll: () => {

}
get customer() {
// fallback to config value when customer state itself it still undefined?
return this.#state?.customer;
}
static dispose = () => {
return PluginInstance.getInstance(0)?.dispose();
};
static disposeAll = () => {
PluginInstance.#instances.forEach((instance) => instance.dispose());
};
static addEventListener = (
...args: Parameters<typeof PluginInstance.prototype.addEventListener>
) => {
return PluginInstance.getInstance(0)?.addEventListener(...args);
};
static setCSSVariable(name: string, value: string) {
return PluginInstance.getInstance(0)?.setCSSVariable(name, value);
}
static get customer() {
return PluginInstance.getInstance(0)?.customer;
}
static get appointment() {
return PluginInstance.getInstance(0)?.appointment;
}
static get intention() {
return PluginInstance.getInstance(0)?.intention;
}
static openDebugger() {
return PluginInstance.getInstance(0)?.openDebugger();
return PluginInstance.#getInstance(0)?.openDebugger();
}

@@ -355,3 +333,3 @@

static #instances: PluginInstance[] = [];
static getInstance = (num: unknown): PluginInstance | undefined => {
static #getInstance = (num: unknown): PluginInstance | undefined => {
if (!isPositiveNumber(num)) return undefined;

@@ -361,4 +339,5 @@

};
static awaitFirstInstance = async () => {
const instance = PluginInstance.getInstance(0);
const instance = PluginInstance.#getInstance(0);
if (instance) return instance;

@@ -393,11 +372,2 @@

static get count() {
return PluginInstance.#instances.length;
}
static readonly EVENT_APPOINTMENT_CANCELLED: PluginEventMessage["type"] = "APPOINTMENT_CANCELLED";
static readonly EVENT_APPOINTMENT_COMPLETED: PluginEventMessage["type"] = "APPOINTMENT_COMPLETED";
static readonly EVENT_APPOINTMENT_CREATED: PluginEventMessage["type"] = "APPOINTMENT_CREATED";
static readonly EVENT_INVITE_ACCEPTED: PluginEventMessage["type"] = "INVITE_ACCEPTED";
static readonly EVENT_APPOINTMENT_EDITED: PluginEventMessage["type"] = "APPOINTMENT_EDITED";
static readonly EVENT_CREATION: InstanceEvent["type"] = "CREATION";

@@ -408,29 +378,18 @@ static readonly EVENT_ERROR: PluginEventMessage["type"] = "ERROR";

static readonly EVENT_MISCONFIGURED: PluginEventMessage["type"] = "MISCONFIGURED";
static readonly EVENT_STEP_SHOWN: StepShownMessage["type"] = "STEP_SHOWN";
static readonly INTENT_CANCEL: PluginIntent = "cancel";
static readonly INTENT_EDIT: PluginIntent = "edit";
static readonly INTENT_INVITE: PluginIntent = "invite";
static readonly INTENT_RESCHEDULE: PluginIntent = "reschedule";
static readonly INTENT_COMPLETE: PluginIntent = "complete";
static readonly INTENT_SCHEDULE: PluginIntent = "schedule";
static readonly INTENT_REJECT: PluginIntent = "reject";
readonly EVENT_APPOINTMENT_CANCELLED = PluginInstance.EVENT_APPOINTMENT_CANCELLED;
readonly EVENT_APPOINTMENT_COMPLETED = PluginInstance.EVENT_APPOINTMENT_COMPLETED;
readonly EVENT_APPOINTMENT_CREATED = PluginInstance.EVENT_APPOINTMENT_CREATED;
readonly EVENT_INVITE_ACCEPTED = PluginInstance.EVENT_INVITE_ACCEPTED;
readonly EVENT_APPOINTMENT_EDITED = PluginInstance.EVENT_APPOINTMENT_EDITED;
readonly EVENT_CREATION = PluginInstance.EVENT_CREATION;
readonly EVENT_ERROR = PluginInstance.EVENT_ERROR;
readonly EVENT_INIT = PluginInstance.EVENT_INIT;
readonly EVENT_LOADED = PluginInstance.EVENT_LOADED;
readonly EVENT_MISCONFIGURED = PluginInstance.EVENT_MISCONFIGURED;
readonly EVENT_STEP_SHOWN = PluginInstance.EVENT_STEP_SHOWN;
readonly INTENT_CANCEL: PluginIntent = PluginInstance.INTENT_CANCEL;
readonly INTENT_EDIT: PluginIntent = PluginInstance.INTENT_EDIT;
readonly INTENT_INVITE: PluginIntent = PluginInstance.INTENT_INVITE;
readonly INTENT_RESCHEDULE: PluginIntent = PluginInstance.INTENT_RESCHEDULE;
readonly INTENT_COMPLETE: PluginIntent = PluginInstance.INTENT_COMPLETE;
readonly INTENT_SCHEDULE: PluginIntent = PluginInstance.INTENT_SCHEDULE;
readonly INTENT_REJECT: PluginIntent = PluginInstance.INTENT_REJECT;
readonly EVENT_APPOINTMENT_CANCELLED: PluginEventMessage["type"] = "APPOINTMENT_CANCELLED";
readonly EVENT_APPOINTMENT_COMPLETED: PluginEventMessage["type"] = "APPOINTMENT_COMPLETED";
readonly EVENT_APPOINTMENT_CREATED: PluginEventMessage["type"] = "APPOINTMENT_CREATED";
readonly EVENT_INVITE_ACCEPTED: PluginEventMessage["type"] = "INVITE_ACCEPTED";
readonly EVENT_APPOINTMENT_EDITED: PluginEventMessage["type"] = "APPOINTMENT_EDITED";
readonly EVENT_STEP_SHOWN: StepShownMessage["type"] = "STEP_SHOWN";
readonly INTENT_CANCEL: PluginIntent = "cancel";
readonly INTENT_EDIT: PluginIntent = "edit";
readonly INTENT_INVITE: PluginIntent = "invite";
readonly INTENT_RESCHEDULE: PluginIntent = "reschedule";
readonly INTENT_COMPLETE: PluginIntent = "complete";
readonly INTENT_SCHEDULE: PluginIntent = "schedule";
readonly INTENT_REJECT: PluginIntent = "reject";
}

@@ -437,0 +396,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet