Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,851 changes: 849 additions & 1,002 deletions src/Umbraco.Community.SimpleTrees.Client/package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions src/Umbraco.Community.SimpleTrees.Client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
"devDependencies": {
"lit": "^3.3.1",
"@hey-api/client-fetch": "^0.13.1",
"@hey-api/openapi-ts": "^0.82.4",
"@hey-api/openapi-ts": "^0.86.1",
"@umbraco-cms/backoffice": "^15.4.4",
"typescript": "^5.9.2",
"vite": "^7.1.4"
"typescript": "^5.9.3",
"vite": "^7.1.11"
},
"volta": {
"node": "22.12.0"
Expand Down
10 changes: 5 additions & 5 deletions src/Umbraco.Community.SimpleTrees.Client/src/api/client.gen.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts

import type { ClientOptions } from './types.gen';
import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from './client';
import { type ClientOptions, type Config, createClient, createConfig } from './client';
import type { ClientOptions as ClientOptions2 } from './types.gen';

/**
* The `createClientConfig()` function will be called on client initialization
Expand All @@ -11,9 +11,9 @@ import { type Config, type ClientOptions as DefaultClientOptions, createClient,
* `setConfig()`. This is useful for example if you're using Next.js
* to ensure your client always has the correct values.
*/
export type CreateClientConfig<T extends DefaultClientOptions = ClientOptions> = (override?: Config<DefaultClientOptions & T>) => Config<Required<DefaultClientOptions> & T>;
export type CreateClientConfig<T extends ClientOptions = ClientOptions2> = (override?: Config<ClientOptions & T>) => Config<Required<ClientOptions> & T>;

export const client = createClient(createConfig<ClientOptions>({
export const client = createClient(createConfig<ClientOptions2>({
baseUrl: 'http://localhost:54813',
throwOnError: true
}));
}));
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { createSseClient } from '../core/serverSentEvents.gen';
import type { HttpMethod } from '../core/types.gen';
import { getValidRequestBody } from '../core/utils.gen';
import type {
Client,
Config,
Expand Down Expand Up @@ -85,7 +86,7 @@ export const createClient = (config: Config = {}): Client => {

let request = new Request(url, requestInit);

for (const fn of interceptors.request._fns) {
for (const fn of interceptors.request.fns) {
if (fn) {
request = await fn(request, opts);
}
Expand All @@ -96,7 +97,7 @@ export const createClient = (config: Config = {}): Client => {
const _fetch = opts.fetch!;
let response = await _fetch(request);

for (const fn of interceptors.response._fns) {
for (const fn of interceptors.response.fns) {
if (fn) {
response = await fn(response, request, opts);
}
Expand Down Expand Up @@ -191,7 +192,7 @@ export const createClient = (config: Config = {}): Client => {
const error = jsonError ?? textError;
let finalError = error;

for (const fn of interceptors.error._fns) {
for (const fn of interceptors.error.fns) {
if (fn) {
finalError = (await fn(error, response, request, opts)) as string;
}
Expand All @@ -212,26 +213,6 @@ export const createClient = (config: Config = {}): Client => {
};
};

function getValidRequestBody(options: ResolvedRequestOptions) {
const hasBody = options.body !== undefined;
const isSerializedBody = hasBody && options.bodySerializer;

if (isSerializedBody) {
const hasSerializedBody =
options.serializedBody !== undefined && options.serializedBody !== '';

return hasSerializedBody ? options.serializedBody : null;
}

// plain/text body
if (hasBody) {
return options.body;
}

// no body was provided
return undefined;
}

const makeMethodFn =
(method: Uppercase<HttpMethod>) => (options: RequestOptions) =>
request({ ...options, method });
Expand All @@ -246,7 +227,7 @@ export const createClient = (config: Config = {}): Client => {
method,
onRequest: async (url, init) => {
let request = new Request(url, init);
for (const fn of interceptors.request._fns) {
for (const fn of interceptors.request.fns) {
if (fn) {
request = await fn(request, opts);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export {
urlSearchParamsBodySerializer,
} from '../core/bodySerializer.gen';
export { buildClientParams } from '../core/params.gen';
export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen';
export { createClient } from './client.gen';
export type {
Client,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,17 +183,27 @@ export const mergeConfigs = (a: Config, b: Config): Config => {
return config;
};

const headersEntries = (headers: Headers): Array<[string, string]> => {
const entries: Array<[string, string]> = [];
headers.forEach((value, key) => {
entries.push([key, value]);
});
return entries;
};

export const mergeHeaders = (
...headers: Array<Required<Config>['headers'] | undefined>
): Headers => {
const mergedHeaders = new Headers();
for (const header of headers) {
if (!header || typeof header !== 'object') {
if (!header) {
continue;
}

const iterator =
header instanceof Headers ? header.entries() : Object.entries(header);
header instanceof Headers
? headersEntries(header)
: Object.entries(header);

for (const [key, value] of iterator) {
if (value === null) {
Expand Down Expand Up @@ -234,67 +244,61 @@ type ResInterceptor<Res, Req, Options> = (
) => Res | Promise<Res>;

class Interceptors<Interceptor> {
_fns: (Interceptor | null)[];
fns: Array<Interceptor | null> = [];

constructor() {
this._fns = [];
clear(): void {
this.fns = [];
}

clear() {
this._fns = [];
}

getInterceptorIndex(id: number | Interceptor): number {
if (typeof id === 'number') {
return this._fns[id] ? id : -1;
} else {
return this._fns.indexOf(id);
eject(id: number | Interceptor): void {
const index = this.getInterceptorIndex(id);
if (this.fns[index]) {
this.fns[index] = null;
}
}
exists(id: number | Interceptor) {

exists(id: number | Interceptor): boolean {
const index = this.getInterceptorIndex(id);
return !!this._fns[index];
return Boolean(this.fns[index]);
}

eject(id: number | Interceptor) {
const index = this.getInterceptorIndex(id);
if (this._fns[index]) {
this._fns[index] = null;
getInterceptorIndex(id: number | Interceptor): number {
if (typeof id === 'number') {
return this.fns[id] ? id : -1;
}
return this.fns.indexOf(id);
}

update(id: number | Interceptor, fn: Interceptor) {
update(
id: number | Interceptor,
fn: Interceptor,
): number | Interceptor | false {
const index = this.getInterceptorIndex(id);
if (this._fns[index]) {
this._fns[index] = fn;
if (this.fns[index]) {
this.fns[index] = fn;
return id;
} else {
return false;
}
return false;
}

use(fn: Interceptor) {
this._fns = [...this._fns, fn];
return this._fns.length - 1;
use(fn: Interceptor): number {
this.fns.push(fn);
return this.fns.length - 1;
}
}

// `createInterceptors()` response, meant for external use as it does not
// expose internals
export interface Middleware<Req, Res, Err, Options> {
error: Pick<
Interceptors<ErrInterceptor<Err, Res, Req, Options>>,
'eject' | 'use'
>;
request: Pick<Interceptors<ReqInterceptor<Req, Options>>, 'eject' | 'use'>;
response: Pick<
Interceptors<ResInterceptor<Res, Req, Options>>,
'eject' | 'use'
>;
error: Interceptors<ErrInterceptor<Err, Res, Req, Options>>;
request: Interceptors<ReqInterceptor<Req, Options>>;
response: Interceptors<ResInterceptor<Res, Req, Options>>;
}

// do not add `Middleware` as return type so we can use _fns internally
export const createInterceptors = <Req, Res, Err, Options>() => ({
export const createInterceptors = <Req, Res, Err, Options>(): Middleware<
Req,
Res,
Err,
Options
> => ({
error: new Interceptors<ErrInterceptor<Err, Res, Req, Options>>(),
request: new Interceptors<ReqInterceptor<Req, Options>>(),
response: new Interceptors<ResInterceptor<Res, Req, Options>>(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// This file is auto-generated by @hey-api/openapi-ts

/**
* JSON-friendly union that mirrors what Pinia Colada can hash.
*/
export type JsonValue =
| null
| string
| number
| boolean
| JsonValue[]
| { [key: string]: JsonValue };

/**
* Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes.
*/
export const queryKeyJsonReplacer = (_key: string, value: unknown) => {
if (
value === undefined ||
typeof value === 'function' ||
typeof value === 'symbol'
) {
return undefined;
}
if (typeof value === 'bigint') {
return value.toString();
}
if (value instanceof Date) {
return value.toISOString();
}
return value;
};

/**
* Safely stringifies a value and parses it back into a JsonValue.
*/
export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => {
try {
const json = JSON.stringify(input, queryKeyJsonReplacer);
if (json === undefined) {
return undefined;
}
return JSON.parse(json) as JsonValue;
} catch {
return undefined;
}
};

/**
* Detects plain objects (including objects with a null prototype).
*/
const isPlainObject = (value: unknown): value is Record<string, unknown> => {
if (value === null || typeof value !== 'object') {
return false;
}
const prototype = Object.getPrototypeOf(value as object);
return prototype === Object.prototype || prototype === null;
};

/**
* Turns URLSearchParams into a sorted JSON object for deterministic keys.
*/
const serializeSearchParams = (params: URLSearchParams): JsonValue => {
const entries = Array.from(params.entries()).sort(([a], [b]) =>
a.localeCompare(b),
);
const result: Record<string, JsonValue> = {};

for (const [key, value] of entries) {
const existing = result[key];
if (existing === undefined) {
result[key] = value;
continue;
}

if (Array.isArray(existing)) {
(existing as string[]).push(value);
} else {
result[key] = [existing, value];
}
}

return result;
};

/**
* Normalizes any accepted value into a JSON-friendly shape for query keys.
*/
export const serializeQueryKeyValue = (
value: unknown,
): JsonValue | undefined => {
if (value === null) {
return null;
}

if (
typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'boolean'
) {
return value;
}

if (
value === undefined ||
typeof value === 'function' ||
typeof value === 'symbol'
) {
return undefined;
}

if (typeof value === 'bigint') {
return value.toString();
}

if (value instanceof Date) {
return value.toISOString();
}

if (Array.isArray(value)) {
return stringifyToJsonValue(value);
}

if (
typeof URLSearchParams !== 'undefined' &&
value instanceof URLSearchParams
) {
return serializeSearchParams(value);
}

if (isPlainObject(value)) {
return stringifyToJsonValue(value);
}

return undefined;
};
Loading
Loading