Files
APIlite/src/typescript.tpl.php

300 lines
8.6 KiB
PHP

<?php
$mapType = function (?string $type): string {
if (!is_string($type) || $type === '') {
return 'unknown';
}
$type = strtolower(ltrim($type, '\\'));
return match ($type) {
'int', 'integer', 'float', 'double' => 'number',
'string' => 'string',
'bool', 'boolean' => 'boolean',
'array', 'iterable' => 'unknown[]',
'object', 'stdclass' => 'Record<string, unknown>',
'mixed', 'resource' => 'unknown',
'null', 'void' => 'null',
'scalar' => 'string | number | boolean',
'callable', 'closure' => '(...args: unknown[]) => unknown',
'never' => 'never',
default => 'unknown',
};
};
$mapUnionType = function (mixed $type) use ($mapType): string {
if (is_array($type)) {
$parts = array();
foreach ($type as $singleType) {
$parts[] = $mapType(is_string($singleType) ? $singleType : null);
}
$parts = array_values(array_unique($parts));
return empty($parts) ? 'unknown' : implode(' | ', $parts);
}
return $mapType(is_string($type) ? $type : null);
};
$normalizeTypeStructures = function (mixed $structure): array {
if (!is_array($structure)) {
return array();
}
if (array_key_exists('type', $structure) && array_key_exists('properties', $structure)) {
return array($structure);
}
return array_values(array_filter($structure, 'is_array'));
};
$findTypeStructure = function (?string $type, mixed $structure) use ($normalizeTypeStructures): array|null {
if (!is_string($type) || $type === '') {
return null;
}
$type = ltrim($type, '\\');
foreach ($normalizeTypeStructures($structure) as $one_structure) {
if (($one_structure['type'] ?? null) === $type) {
return $one_structure;
}
}
return null;
};
$mapTypeWithStructure = null;
$mapUnionTypeWithStructure = null;
$mapTypeWithStructure = function (?string $type, mixed $structure) use ($mapType, &$mapUnionTypeWithStructure): string {
if (is_array($structure) && array_key_exists('type', $structure) && array_key_exists('properties', $structure)) {
$properties = array();
foreach (($structure['properties'] ?? array()) as $property) {
if (!is_array($property) || !isset($property['name'])) {
continue;
}
$propertyType = $mapUnionTypeWithStructure($property['type'] ?? null, $property['type_structure'] ?? null);
if (!empty($property['nullable']) && strpos($propertyType, 'null') === false) {
$propertyType .= ' | null';
}
$properties[] = $property['name'] . ': ' . $propertyType;
}
return empty($properties) ? 'Record<string, unknown>' : '{ ' . implode('; ', $properties) . ' }';
}
return $mapType($type);
};
$mapUnionTypeWithStructure = function (mixed $type, mixed $structure) use ($mapType, $findTypeStructure, &$mapTypeWithStructure): string {
if (is_array($type)) {
$parts = array();
foreach ($type as $singleType) {
$typeName = is_string($singleType) ? $singleType : null;
$parts[] = $mapTypeWithStructure($typeName, $findTypeStructure($typeName, $structure));
}
$parts = array_values(array_unique($parts));
return empty($parts) ? 'unknown' : implode(' | ', $parts);
}
return $mapTypeWithStructure(is_string($type) ? $type : null, $structure);
};
?>
/**
* Generated by APIlite
* https://gitea.tpsoft.org/TPsoft.org/APIlite
*
* <?php echo date('Y-m-d H:i:s'); ?>
*/
export interface APIliteActionResponse<T> {
status: 'OK';
data: T;
msg: string;
}
export interface APIliteErrorResponse {
status: 'ERROR';
msg: string;
}
export interface APIliteMethodParam {
name: string;
type: string | null;
optional: boolean;
default: unknown;
doc: string | null;
}
export interface APIliteMethodDoc {
name: string;
doc: string | null;
description: string | null;
params: APIliteMethodParam[];
return: string | string[] | null;
return_structure: APIliteTypeStructure | APIliteTypeStructure[] | null;
}
export interface APIliteTypeStructureProperty {
name: string;
type: string | string[] | null;
nullable: boolean;
type_structure: APIliteTypeStructure | APIliteTypeStructure[] | null;
}
export interface APIliteTypeStructure {
type: string;
recursive?: boolean;
properties: APIliteTypeStructureProperty[];
}
export interface APIliteHelpResponse {
name: string;
html_version: string;
javascript_version: string;
typescript_version: string;
actions: APIliteMethodDoc[];
status: string;
data: string;
msg: string;
}
type APIliteRequestHeaders = Partial<Record<string, string>>;
class <?php echo $this->apiName; ?> {
endpoint: string = <?php echo sprintf(substr($this->endpoint, 0, 4) == 'http' ? '"%s"' : '%s', $this->endpoint); ?>;
private readonly bearerStorageKey: string = 'apilite_bearer_token';
private normalizeBearerToken(token: string | null | undefined): string | null {
if (typeof token !== 'string') {
return null;
}
const normalizedToken = token.trim();
return normalizedToken === '' ? null : normalizedToken;
}
private getStorage(): Storage | null {
try {
if (typeof window !== 'undefined' && typeof window.localStorage !== 'undefined') {
return window.localStorage;
}
} catch {
return null;
}
return null;
}
private getBearerToken(): string | null {
const storage = this.getStorage();
if (storage === null) {
return null;
}
return this.normalizeBearerToken(storage.getItem(this.bearerStorageKey));
}
private getRequestHeaders(headers: APIliteRequestHeaders = {}): APIliteRequestHeaders {
const requestHeaders: APIliteRequestHeaders = { ...headers };
if (typeof requestHeaders.Authorization === 'undefined') {
const token = this.getBearerToken();
if (token !== null) {
requestHeaders.Authorization = `Bearer ${token}`;
}
}
return requestHeaders;
}
private applyHeaders(xhttp: XMLHttpRequest, headers: APIliteRequestHeaders): void {
Object.keys(headers).forEach((key) => {
const value = headers[key];
if (typeof value === 'undefined' || value === null) {
return;
}
xhttp.setRequestHeader(key, value);
});
}
bearerSet(token: string | null): void {
const storage = this.getStorage();
if (storage === null) {
return;
}
const normalizedToken = this.normalizeBearerToken(token);
if (normalizedToken === null) {
storage.removeItem(this.bearerStorageKey);
return;
}
storage.setItem(this.bearerStorageKey, normalizedToken);
}
private call(
method: string,
data: Record<string, unknown>,
callback: (response: APIliteHelpResponse | APIliteActionResponse<unknown> | APIliteErrorResponse) => void
): void {
const xhttp = new XMLHttpRequest();
const headers = this.getRequestHeaders();
xhttp.withCredentials = true;
xhttp.onreadystatechange = function() {
if (this.readyState === 4) {
if (this.status === 200) {
const response = JSON.parse(this.responseText) as APIliteHelpResponse | APIliteActionResponse<unknown> | APIliteErrorResponse;
callback(response);
} else {
callback({ status: 'ERROR', msg: 'HTTP STATUS ' + this.status });
}
}
};
const formData = new FormData();
Object.keys(data).forEach((key) => {
const rawValue = data[key];
if (typeof rawValue === 'undefined') {
return;
}
let value: string | Blob;
if (rawValue instanceof Blob) {
value = rawValue;
} else if (typeof rawValue === 'object' && rawValue !== null) {
value = JSON.stringify(rawValue);
} else {
value = String(rawValue);
}
formData.append(key, value);
});
xhttp.open('POST', this.endpoint + '?action=' + method);
this.applyHeaders(xhttp, headers);
xhttp.send(formData);
}
private callPromise<T>(method: string, data: Record<string, unknown>): Promise<T> {
return new Promise<T>((resolve, reject) => {
this.call(method, data, (response) => {
if (method === '__HELP__') {
resolve(response as T);
return;
}
if (response.status === 'OK') {
resolve(response.data as T);
return;
}
reject(response.msg);
});
});
}
help(): Promise<APIliteHelpResponse> {
return this.callPromise<APIliteHelpResponse>('__HELP__', {});
}
<?php if (is_array($this->methods)) foreach ($this->methods as $method) {
$paramsSignature = array();
$paramsPayload = array();
foreach ($method['params'] as $param) {
$paramType = $mapType($param['type']);
$paramsSignature[] = $param['name'] . ($param['optional'] ? '?' : '') . ': ' . $paramType;
$paramsPayload[] = $param['name'];
}
$returnType = $mapUnionTypeWithStructure($method['return'], $method['return_structure'] ?? null);
?>
<?php echo $method['name']; ?>(<?php echo implode(', ', $paramsSignature); ?>): Promise<APIliteActionResponse<<?php echo $returnType; ?>>> {
return this.callPromise<APIliteActionResponse<<?php echo $returnType; ?>>>('<?php echo $method['name']; ?>', { <?php echo implode(', ', $paramsPayload); ?> });
}
<?php } ?>
}
export default new <?php echo $this->apiName; ?>();