@smplkit/sdk 1.2.7 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/index.cjs +127 -174
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.js +127 -174
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -483,9 +483,9 @@ declare class Rule {
|
|
|
483
483
|
* FlagsClient — management + prescriptive runtime for Smpl Flags.
|
|
484
484
|
*
|
|
485
485
|
* Uses the generated OpenAPI types (`src/generated/flags.d.ts`) via
|
|
486
|
-
* `openapi-fetch` for all HTTP calls. Context type management and
|
|
487
|
-
* context registration use
|
|
488
|
-
*
|
|
486
|
+
* `openapi-fetch` for all flag HTTP calls. Context type management and
|
|
487
|
+
* context registration use the generated app service types
|
|
488
|
+
* (`src/generated/app.d.ts`) via a separate `openapi-fetch` client.
|
|
489
489
|
*/
|
|
490
490
|
|
|
491
491
|
/** Describes a flag definition change. */
|
|
@@ -552,7 +552,7 @@ declare class FlagsClient {
|
|
|
552
552
|
/** @internal */
|
|
553
553
|
private readonly _http;
|
|
554
554
|
/** @internal */
|
|
555
|
-
private readonly
|
|
555
|
+
private readonly _appHttp;
|
|
556
556
|
private _environment;
|
|
557
557
|
private _flagStore;
|
|
558
558
|
private _connected;
|
|
@@ -609,6 +609,8 @@ declare class FlagsClient {
|
|
|
609
609
|
}): Promise<ContextType>;
|
|
610
610
|
/** Update a context type (merge attributes). */
|
|
611
611
|
updateContextType(ctId: string, options: {
|
|
612
|
+
key: string;
|
|
613
|
+
name: string;
|
|
612
614
|
attributes: Record<string, any>;
|
|
613
615
|
}): Promise<ContextType>;
|
|
614
616
|
/** List all context types. */
|
|
@@ -760,6 +762,7 @@ declare class SmplClient {
|
|
|
760
762
|
readonly _service: string | null;
|
|
761
763
|
private _connected;
|
|
762
764
|
private readonly _timeout;
|
|
765
|
+
private readonly _appHttp;
|
|
763
766
|
constructor(options?: SmplClientOptions);
|
|
764
767
|
/**
|
|
765
768
|
* Connect to the smplkit platform.
|
package/dist/index.d.ts
CHANGED
|
@@ -483,9 +483,9 @@ declare class Rule {
|
|
|
483
483
|
* FlagsClient — management + prescriptive runtime for Smpl Flags.
|
|
484
484
|
*
|
|
485
485
|
* Uses the generated OpenAPI types (`src/generated/flags.d.ts`) via
|
|
486
|
-
* `openapi-fetch` for all HTTP calls. Context type management and
|
|
487
|
-
* context registration use
|
|
488
|
-
*
|
|
486
|
+
* `openapi-fetch` for all flag HTTP calls. Context type management and
|
|
487
|
+
* context registration use the generated app service types
|
|
488
|
+
* (`src/generated/app.d.ts`) via a separate `openapi-fetch` client.
|
|
489
489
|
*/
|
|
490
490
|
|
|
491
491
|
/** Describes a flag definition change. */
|
|
@@ -552,7 +552,7 @@ declare class FlagsClient {
|
|
|
552
552
|
/** @internal */
|
|
553
553
|
private readonly _http;
|
|
554
554
|
/** @internal */
|
|
555
|
-
private readonly
|
|
555
|
+
private readonly _appHttp;
|
|
556
556
|
private _environment;
|
|
557
557
|
private _flagStore;
|
|
558
558
|
private _connected;
|
|
@@ -609,6 +609,8 @@ declare class FlagsClient {
|
|
|
609
609
|
}): Promise<ContextType>;
|
|
610
610
|
/** Update a context type (merge attributes). */
|
|
611
611
|
updateContextType(ctId: string, options: {
|
|
612
|
+
key: string;
|
|
613
|
+
name: string;
|
|
612
614
|
attributes: Record<string, any>;
|
|
613
615
|
}): Promise<ContextType>;
|
|
614
616
|
/** List all context types. */
|
|
@@ -760,6 +762,7 @@ declare class SmplClient {
|
|
|
760
762
|
readonly _service: string | null;
|
|
761
763
|
private _connected;
|
|
762
764
|
private readonly _timeout;
|
|
765
|
+
private readonly _appHttp;
|
|
763
766
|
constructor(options?: SmplClientOptions);
|
|
764
767
|
/**
|
|
765
768
|
* Connect to the smplkit platform.
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
import createClient3 from "openapi-fetch";
|
|
3
|
+
|
|
1
4
|
// src/config/client.ts
|
|
2
5
|
import createClient from "openapi-fetch";
|
|
3
6
|
|
|
@@ -664,135 +667,6 @@ var ConfigClient = class {
|
|
|
664
667
|
// src/flags/client.ts
|
|
665
668
|
import createClient2 from "openapi-fetch";
|
|
666
669
|
|
|
667
|
-
// src/auth.ts
|
|
668
|
-
function buildAuthHeader(apiKey) {
|
|
669
|
-
return `Bearer ${apiKey}`;
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
// src/transport.ts
|
|
673
|
-
var SDK_VERSION = "0.0.0";
|
|
674
|
-
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
675
|
-
var Transport = class {
|
|
676
|
-
apiKey;
|
|
677
|
-
timeout;
|
|
678
|
-
constructor(options) {
|
|
679
|
-
this.apiKey = options.apiKey;
|
|
680
|
-
this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
681
|
-
}
|
|
682
|
-
/**
|
|
683
|
-
* Send a GET request.
|
|
684
|
-
*
|
|
685
|
-
* @param url - Fully-qualified URL (e.g. `https://config.smplkit.com/api/v1/configs`).
|
|
686
|
-
* @param params - Optional query parameters.
|
|
687
|
-
* @returns Parsed JSON response body.
|
|
688
|
-
*/
|
|
689
|
-
async get(url, params) {
|
|
690
|
-
return this.request("GET", url, void 0, params);
|
|
691
|
-
}
|
|
692
|
-
/**
|
|
693
|
-
* Send a POST request with a JSON body.
|
|
694
|
-
*
|
|
695
|
-
* @param url - Fully-qualified URL.
|
|
696
|
-
* @param body - JSON-serializable request body.
|
|
697
|
-
* @returns Parsed JSON response body.
|
|
698
|
-
*/
|
|
699
|
-
async post(url, body) {
|
|
700
|
-
return this.request("POST", url, body);
|
|
701
|
-
}
|
|
702
|
-
/**
|
|
703
|
-
* Send a PUT request with a JSON body.
|
|
704
|
-
*
|
|
705
|
-
* @param url - Fully-qualified URL.
|
|
706
|
-
* @param body - JSON-serializable request body.
|
|
707
|
-
* @returns Parsed JSON response body.
|
|
708
|
-
*/
|
|
709
|
-
async put(url, body) {
|
|
710
|
-
return this.request("PUT", url, body);
|
|
711
|
-
}
|
|
712
|
-
/**
|
|
713
|
-
* Send a DELETE request.
|
|
714
|
-
*
|
|
715
|
-
* @param url - Fully-qualified URL.
|
|
716
|
-
* @returns Parsed JSON response body (empty object for 204 responses).
|
|
717
|
-
*/
|
|
718
|
-
async delete(url) {
|
|
719
|
-
return this.request("DELETE", url);
|
|
720
|
-
}
|
|
721
|
-
/**
|
|
722
|
-
* Core request method. Handles headers, timeouts, and error mapping.
|
|
723
|
-
*/
|
|
724
|
-
async request(method, url, body, params) {
|
|
725
|
-
if (params) {
|
|
726
|
-
const searchParams = new URLSearchParams(params);
|
|
727
|
-
url += `?${searchParams.toString()}`;
|
|
728
|
-
}
|
|
729
|
-
const headers = {
|
|
730
|
-
Authorization: buildAuthHeader(this.apiKey),
|
|
731
|
-
"User-Agent": `smplkit-typescript-sdk/${SDK_VERSION}`,
|
|
732
|
-
Accept: "application/vnd.api+json"
|
|
733
|
-
};
|
|
734
|
-
if (body !== void 0) {
|
|
735
|
-
headers["Content-Type"] = "application/vnd.api+json";
|
|
736
|
-
}
|
|
737
|
-
const controller = new AbortController();
|
|
738
|
-
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
739
|
-
let response;
|
|
740
|
-
try {
|
|
741
|
-
response = await fetch(url, {
|
|
742
|
-
method,
|
|
743
|
-
headers,
|
|
744
|
-
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
745
|
-
signal: controller.signal
|
|
746
|
-
});
|
|
747
|
-
} catch (error) {
|
|
748
|
-
clearTimeout(timeoutId);
|
|
749
|
-
if (error instanceof DOMException && error.name === "AbortError") {
|
|
750
|
-
throw new SmplTimeoutError(`Request timed out after ${this.timeout}ms`);
|
|
751
|
-
}
|
|
752
|
-
if (error instanceof TypeError) {
|
|
753
|
-
throw new SmplConnectionError(`Network error: ${error.message}`);
|
|
754
|
-
}
|
|
755
|
-
throw new SmplConnectionError(
|
|
756
|
-
`Request failed: ${error instanceof Error ? error.message : String(error)}`
|
|
757
|
-
);
|
|
758
|
-
} finally {
|
|
759
|
-
clearTimeout(timeoutId);
|
|
760
|
-
}
|
|
761
|
-
if (response.status === 204) {
|
|
762
|
-
return {};
|
|
763
|
-
}
|
|
764
|
-
const responseText = await response.text();
|
|
765
|
-
if (!response.ok) {
|
|
766
|
-
this.throwForStatus(response.status, responseText);
|
|
767
|
-
}
|
|
768
|
-
try {
|
|
769
|
-
return JSON.parse(responseText);
|
|
770
|
-
} catch {
|
|
771
|
-
throw new SmplError(`Invalid JSON response: ${responseText}`, response.status, responseText);
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
/**
|
|
775
|
-
* Map HTTP error status codes to typed SDK exceptions.
|
|
776
|
-
*
|
|
777
|
-
* @throws {SmplNotFoundError} On 404.
|
|
778
|
-
* @throws {SmplConflictError} On 409.
|
|
779
|
-
* @throws {SmplValidationError} On 422.
|
|
780
|
-
* @throws {SmplError} On any other non-2xx status.
|
|
781
|
-
*/
|
|
782
|
-
throwForStatus(status, body) {
|
|
783
|
-
switch (status) {
|
|
784
|
-
case 404:
|
|
785
|
-
throw new SmplNotFoundError(body, 404, body);
|
|
786
|
-
case 409:
|
|
787
|
-
throw new SmplConflictError(body, 409, body);
|
|
788
|
-
case 422:
|
|
789
|
-
throw new SmplValidationError(body, 422, body);
|
|
790
|
-
default:
|
|
791
|
-
throw new SmplError(`HTTP ${status}: ${body}`, status, body);
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
};
|
|
795
|
-
|
|
796
670
|
// src/flags/models.ts
|
|
797
671
|
var Flag = class {
|
|
798
672
|
/** UUID of the flag. */
|
|
@@ -1168,7 +1042,7 @@ var FlagsClient = class {
|
|
|
1168
1042
|
/** @internal */
|
|
1169
1043
|
_http;
|
|
1170
1044
|
/** @internal */
|
|
1171
|
-
|
|
1045
|
+
_appHttp;
|
|
1172
1046
|
// Runtime state
|
|
1173
1047
|
_environment = null;
|
|
1174
1048
|
_flagStore = {};
|
|
@@ -1188,28 +1062,36 @@ var FlagsClient = class {
|
|
|
1188
1062
|
this._apiKey = apiKey;
|
|
1189
1063
|
this._ensureWs = ensureWs;
|
|
1190
1064
|
const ms = timeout ?? 3e4;
|
|
1065
|
+
const fetchWithTimeout = async (request) => {
|
|
1066
|
+
const controller = new AbortController();
|
|
1067
|
+
const timer = setTimeout(() => controller.abort(), ms);
|
|
1068
|
+
try {
|
|
1069
|
+
return await fetch(new Request(request, { signal: controller.signal }));
|
|
1070
|
+
} catch (err) {
|
|
1071
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
1072
|
+
throw new SmplTimeoutError(`Request timed out after ${ms}ms`);
|
|
1073
|
+
}
|
|
1074
|
+
throw err;
|
|
1075
|
+
} finally {
|
|
1076
|
+
clearTimeout(timer);
|
|
1077
|
+
}
|
|
1078
|
+
};
|
|
1191
1079
|
this._http = createClient2({
|
|
1192
1080
|
baseUrl: FLAGS_BASE_URL,
|
|
1193
1081
|
headers: {
|
|
1194
1082
|
Authorization: `Bearer ${apiKey}`,
|
|
1195
1083
|
Accept: "application/json"
|
|
1196
1084
|
},
|
|
1197
|
-
fetch:
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
throw err;
|
|
1207
|
-
} finally {
|
|
1208
|
-
clearTimeout(timer);
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1085
|
+
fetch: fetchWithTimeout
|
|
1086
|
+
});
|
|
1087
|
+
this._appHttp = createClient2({
|
|
1088
|
+
baseUrl: APP_BASE_URL,
|
|
1089
|
+
headers: {
|
|
1090
|
+
Authorization: `Bearer ${apiKey}`,
|
|
1091
|
+
Accept: "application/json"
|
|
1092
|
+
},
|
|
1093
|
+
fetch: fetchWithTimeout
|
|
1211
1094
|
});
|
|
1212
|
-
this._transport = new Transport({ apiKey, timeout: ms });
|
|
1213
1095
|
}
|
|
1214
1096
|
// ------------------------------------------------------------------
|
|
1215
1097
|
// Management methods
|
|
@@ -1324,40 +1206,87 @@ var FlagsClient = class {
|
|
|
1324
1206
|
return this._resourceToModel(data.data);
|
|
1325
1207
|
}
|
|
1326
1208
|
// ------------------------------------------------------------------
|
|
1327
|
-
// Context type management (
|
|
1209
|
+
// Context type management (via generated app client)
|
|
1328
1210
|
// ------------------------------------------------------------------
|
|
1329
1211
|
/** Create a context type. */
|
|
1330
1212
|
async createContextType(key, options) {
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1213
|
+
let data;
|
|
1214
|
+
try {
|
|
1215
|
+
const result = await this._appHttp.POST("/api/v1/context_types", {
|
|
1216
|
+
body: {
|
|
1217
|
+
data: { type: "context_type", attributes: { key, name: options.name } }
|
|
1218
|
+
}
|
|
1219
|
+
});
|
|
1220
|
+
if (result.error !== void 0)
|
|
1221
|
+
await checkError2(result.response, "Failed to create context type");
|
|
1222
|
+
data = result.data;
|
|
1223
|
+
} catch (err) {
|
|
1224
|
+
wrapFetchError2(err);
|
|
1225
|
+
}
|
|
1226
|
+
if (!data || !data.data) throw new SmplValidationError("Failed to create context type");
|
|
1227
|
+
return this._parseContextType(data.data);
|
|
1336
1228
|
}
|
|
1337
1229
|
/** Update a context type (merge attributes). */
|
|
1338
1230
|
async updateContextType(ctId, options) {
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1231
|
+
let data;
|
|
1232
|
+
try {
|
|
1233
|
+
const result = await this._appHttp.PUT("/api/v1/context_types/{id}", {
|
|
1234
|
+
params: { path: { id: ctId } },
|
|
1235
|
+
body: {
|
|
1236
|
+
data: {
|
|
1237
|
+
type: "context_type",
|
|
1238
|
+
attributes: { key: options.key, name: options.name, attributes: options.attributes }
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
});
|
|
1242
|
+
if (result.error !== void 0)
|
|
1243
|
+
await checkError2(result.response, `Failed to update context type ${ctId}`);
|
|
1244
|
+
data = result.data;
|
|
1245
|
+
} catch (err) {
|
|
1246
|
+
wrapFetchError2(err);
|
|
1247
|
+
}
|
|
1248
|
+
if (!data || !data.data) throw new SmplValidationError(`Failed to update context type ${ctId}`);
|
|
1249
|
+
return this._parseContextType(data.data);
|
|
1344
1250
|
}
|
|
1345
1251
|
/** List all context types. */
|
|
1346
1252
|
async listContextTypes() {
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1253
|
+
let data;
|
|
1254
|
+
try {
|
|
1255
|
+
const result = await this._appHttp.GET("/api/v1/context_types");
|
|
1256
|
+
if (result.error !== void 0)
|
|
1257
|
+
await checkError2(result.response, "Failed to list context types");
|
|
1258
|
+
data = result.data;
|
|
1259
|
+
} catch (err) {
|
|
1260
|
+
wrapFetchError2(err);
|
|
1261
|
+
}
|
|
1262
|
+
if (!data || !data.data) throw new SmplValidationError("Failed to list context types");
|
|
1263
|
+
return data.data.map((item) => this._parseContextType(item));
|
|
1350
1264
|
}
|
|
1351
1265
|
/** Delete a context type. */
|
|
1352
1266
|
async deleteContextType(ctId) {
|
|
1353
|
-
|
|
1267
|
+
try {
|
|
1268
|
+
const result = await this._appHttp.DELETE("/api/v1/context_types/{id}", {
|
|
1269
|
+
params: { path: { id: ctId } }
|
|
1270
|
+
});
|
|
1271
|
+
if (result.error !== void 0 && result.response.status !== 204)
|
|
1272
|
+
await checkError2(result.response, `Failed to delete context type ${ctId}`);
|
|
1273
|
+
} catch (err) {
|
|
1274
|
+
wrapFetchError2(err);
|
|
1275
|
+
}
|
|
1354
1276
|
}
|
|
1355
1277
|
/** List context instances filtered by context type key. */
|
|
1356
1278
|
async listContexts(options) {
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1279
|
+
let data;
|
|
1280
|
+
try {
|
|
1281
|
+
const result = await this._appHttp.GET("/api/v1/contexts", {
|
|
1282
|
+
params: { query: { "filter[context_type_id]": options.contextTypeKey } }
|
|
1283
|
+
});
|
|
1284
|
+
if (result.error !== void 0) await checkError2(result.response, "Failed to list contexts");
|
|
1285
|
+
data = result.data;
|
|
1286
|
+
} catch (err) {
|
|
1287
|
+
wrapFetchError2(err);
|
|
1288
|
+
}
|
|
1289
|
+
return data?.data ?? [];
|
|
1361
1290
|
}
|
|
1362
1291
|
// ------------------------------------------------------------------
|
|
1363
1292
|
// Runtime: typed flag handles
|
|
@@ -1647,8 +1576,14 @@ var FlagsClient = class {
|
|
|
1647
1576
|
const batch = this._contextBuffer.drain();
|
|
1648
1577
|
if (batch.length === 0) return;
|
|
1649
1578
|
try {
|
|
1650
|
-
await this.
|
|
1651
|
-
|
|
1579
|
+
await this._appHttp.POST("/api/v1/contexts/bulk", {
|
|
1580
|
+
body: {
|
|
1581
|
+
contexts: batch.map((ctx) => ({
|
|
1582
|
+
type: ctx.type,
|
|
1583
|
+
key: ctx.key,
|
|
1584
|
+
attributes: ctx.attributes
|
|
1585
|
+
}))
|
|
1586
|
+
}
|
|
1652
1587
|
});
|
|
1653
1588
|
} catch {
|
|
1654
1589
|
}
|
|
@@ -1894,6 +1829,7 @@ var SmplClient = class {
|
|
|
1894
1829
|
_service;
|
|
1895
1830
|
_connected = false;
|
|
1896
1831
|
_timeout;
|
|
1832
|
+
_appHttp;
|
|
1897
1833
|
constructor(options = {}) {
|
|
1898
1834
|
const apiKey = resolveApiKey(options.apiKey);
|
|
1899
1835
|
this._apiKey = apiKey;
|
|
@@ -1904,6 +1840,28 @@ var SmplClient = class {
|
|
|
1904
1840
|
this._environment = environment;
|
|
1905
1841
|
this._service = options.service || process.env.SMPLKIT_SERVICE || null;
|
|
1906
1842
|
this._timeout = options.timeout ?? 3e4;
|
|
1843
|
+
const ms = this._timeout;
|
|
1844
|
+
this._appHttp = createClient3({
|
|
1845
|
+
baseUrl: APP_BASE_URL2,
|
|
1846
|
+
headers: {
|
|
1847
|
+
Authorization: `Bearer ${apiKey}`,
|
|
1848
|
+
Accept: "application/json"
|
|
1849
|
+
},
|
|
1850
|
+
fetch: async (request) => {
|
|
1851
|
+
const controller = new AbortController();
|
|
1852
|
+
const timer = setTimeout(() => controller.abort(), ms);
|
|
1853
|
+
try {
|
|
1854
|
+
return await fetch(new Request(request, { signal: controller.signal }));
|
|
1855
|
+
} catch (err) {
|
|
1856
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
1857
|
+
throw new SmplTimeoutError(`Request timed out after ${ms}ms`);
|
|
1858
|
+
}
|
|
1859
|
+
throw err;
|
|
1860
|
+
} finally {
|
|
1861
|
+
clearTimeout(timer);
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
});
|
|
1907
1865
|
this.config = new ConfigClient(apiKey, this._timeout);
|
|
1908
1866
|
this.flags = new FlagsClient(apiKey, () => this._ensureWs(), this._timeout);
|
|
1909
1867
|
this.config._getSharedWs = () => this._ensureWs();
|
|
@@ -1930,13 +1888,8 @@ var SmplClient = class {
|
|
|
1930
1888
|
/** @internal */
|
|
1931
1889
|
async _registerServiceContext() {
|
|
1932
1890
|
try {
|
|
1933
|
-
await
|
|
1934
|
-
|
|
1935
|
-
headers: {
|
|
1936
|
-
Authorization: `Bearer ${this._apiKey}`,
|
|
1937
|
-
"Content-Type": "application/json"
|
|
1938
|
-
},
|
|
1939
|
-
body: JSON.stringify({
|
|
1891
|
+
await this._appHttp.POST("/api/v1/contexts/bulk", {
|
|
1892
|
+
body: {
|
|
1940
1893
|
contexts: [
|
|
1941
1894
|
{
|
|
1942
1895
|
type: "service",
|
|
@@ -1944,7 +1897,7 @@ var SmplClient = class {
|
|
|
1944
1897
|
attributes: { name: this._service }
|
|
1945
1898
|
}
|
|
1946
1899
|
]
|
|
1947
|
-
}
|
|
1900
|
+
}
|
|
1948
1901
|
});
|
|
1949
1902
|
} catch {
|
|
1950
1903
|
}
|