@tinycloud/sdk-core 3.8.0-beta.1 → 3.8.0-beta.15
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/dist/delegations/index.cjs +67 -6
- package/dist/delegations/index.cjs.map +1 -1
- package/dist/delegations/index.d.cts +4 -2
- package/dist/delegations/index.d.ts +4 -2
- package/dist/delegations/index.js +68 -7
- package/dist/delegations/index.js.map +1 -1
- package/dist/delegations/issued.cjs +697 -0
- package/dist/delegations/issued.cjs.map +1 -0
- package/dist/delegations/issued.d.cts +54 -0
- package/dist/delegations/issued.d.ts +54 -0
- package/dist/delegations/issued.js +674 -0
- package/dist/delegations/issued.js.map +1 -0
- package/dist/index.cjs +149 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -4
- package/dist/index.d.ts +20 -4
- package/dist/index.js +155 -16
- package/dist/index.js.map +1 -1
- package/dist/{recipient-types.schema-BApcOn1t.d.cts → recipient-types.schema-BIJcihno.d.cts} +42 -1741
- package/dist/{recipient-types.schema-BApcOn1t.d.ts → recipient-types.schema-Ebi5aliq.d.ts} +42 -1741
- package/dist/types.schema-BmN-OnCA.d.cts +1763 -0
- package/dist/types.schema-BmN-OnCA.d.ts +1763 -0
- package/package.json +9 -4
package/dist/index.js
CHANGED
|
@@ -6177,7 +6177,11 @@ var DelegationErrorCodes = {
|
|
|
6177
6177
|
KV_SERVICE_UNAVAILABLE: "KV_SERVICE_UNAVAILABLE",
|
|
6178
6178
|
DATA_FETCH_FAILED: "DATA_FETCH_FAILED",
|
|
6179
6179
|
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
6180
|
-
UNSUPPORTED_OPERATION: "UNSUPPORTED_OPERATION"
|
|
6180
|
+
UNSUPPORTED_OPERATION: "UNSUPPORTED_OPERATION",
|
|
6181
|
+
/** The node answered 503: the delegation chain is too deep to evaluate right now. Retry later. */
|
|
6182
|
+
CHAIN_TOO_DEEP: "CHAIN_TOO_DEEP",
|
|
6183
|
+
/** The node answered 400 "invalid delegation query cursor": malformed, foreign or from another filter. Start the listing again without it. */
|
|
6184
|
+
INVALID_CURSOR: "INVALID_CURSOR"
|
|
6181
6185
|
};
|
|
6182
6186
|
var DelegationSchema = z4.object({
|
|
6183
6187
|
/** Content identifier (CID) of the delegation */
|
|
@@ -6254,7 +6258,7 @@ var AccountDelegationPageSchema = z4.object({
|
|
|
6254
6258
|
nextCursor: z4.string().optional()
|
|
6255
6259
|
}).strict();
|
|
6256
6260
|
var AccountDelegationQueryOptionsSchema = z4.object({
|
|
6257
|
-
direction: z4.enum(["granted", "received", "all"]).optional(),
|
|
6261
|
+
direction: z4.enum(["granted", "received", "all", "issued"]).optional(),
|
|
6258
6262
|
status: z4.enum(["active", "pending", "expired", "revoked", "ancestor_revoked"]).optional(),
|
|
6259
6263
|
space: z4.string().trim().min(1).optional(),
|
|
6260
6264
|
limit: z4.number().int().min(1).max(100).optional(),
|
|
@@ -6440,6 +6444,8 @@ var DelegationManagerConfigSchema = z4.object({
|
|
|
6440
6444
|
).optional(),
|
|
6441
6445
|
/** Optional owner-signed authorization for direct CID revocation */
|
|
6442
6446
|
signOwnerRevocation: z4.custom().optional(),
|
|
6447
|
+
/** Optional signer for proofless invocations by the session's own did:key (issued-delegation list and revoke) */
|
|
6448
|
+
sessionInvocationSigner: z4.custom().optional(),
|
|
6443
6449
|
/** Optional custom fetch implementation */
|
|
6444
6450
|
fetch: z4.unknown().refine(
|
|
6445
6451
|
(val) => val === void 0 || typeof val === "function",
|
|
@@ -6535,7 +6541,7 @@ var CreateDelegationWasmResultSchema = z4.object({
|
|
|
6535
6541
|
resources: z4.array(DelegatedResourceSchema)
|
|
6536
6542
|
});
|
|
6537
6543
|
|
|
6538
|
-
// src/delegations/
|
|
6544
|
+
// src/delegations/shared.ts
|
|
6539
6545
|
var DelegationAction = {
|
|
6540
6546
|
REVOKE: "tinycloud.delegation/revoke",
|
|
6541
6547
|
STATUS: "tinycloud.delegation/status",
|
|
@@ -6552,6 +6558,8 @@ function createError(code3, message, cause, meta) {
|
|
|
6552
6558
|
meta
|
|
6553
6559
|
};
|
|
6554
6560
|
}
|
|
6561
|
+
|
|
6562
|
+
// src/delegations/DelegationManager.ts
|
|
6555
6563
|
async function readInvokedJson(response, action) {
|
|
6556
6564
|
const text3 = await response.text();
|
|
6557
6565
|
if (text3.trim() === "") {
|
|
@@ -6574,11 +6582,13 @@ var DelegationManager = class {
|
|
|
6574
6582
|
* @param config - Configuration including hosts, session, and invoke function
|
|
6575
6583
|
*/
|
|
6576
6584
|
constructor(config) {
|
|
6585
|
+
this.issuedState = { issuedQuerySupported: false };
|
|
6577
6586
|
this.hosts = config.hosts;
|
|
6578
6587
|
this.session = config.session;
|
|
6579
6588
|
this.invoke = config.invoke;
|
|
6580
6589
|
this.invokeAny = config.invokeAny;
|
|
6581
6590
|
this.signOwnerRevocation = config.signOwnerRevocation;
|
|
6591
|
+
this.sessionInvocationSigner = config.sessionInvocationSigner;
|
|
6582
6592
|
this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
|
|
6583
6593
|
this.accountSpaceId = config.accountSpaceId;
|
|
6584
6594
|
}
|
|
@@ -6801,8 +6811,14 @@ var DelegationManager = class {
|
|
|
6801
6811
|
};
|
|
6802
6812
|
}
|
|
6803
6813
|
}
|
|
6804
|
-
/**
|
|
6805
|
-
|
|
6814
|
+
/**
|
|
6815
|
+
* Query lifecycle-complete delegation history for the authenticated account.
|
|
6816
|
+
*
|
|
6817
|
+
* `scope.spaces` lists the spaces to sign `tinycloud.delegation/list` for, in order. Omitted, the
|
|
6818
|
+
* account space is tried, then the session's own space. A caller that knows which of them the session
|
|
6819
|
+
* holds passes only those; an empty list sends nothing and answers PERMISSION_DENIED.
|
|
6820
|
+
*/
|
|
6821
|
+
async query(options2 = {}, scope = {}) {
|
|
6806
6822
|
const validated = AccountDelegationQueryOptionsSchema.safeParse(options2);
|
|
6807
6823
|
if (!validated.success) {
|
|
6808
6824
|
return {
|
|
@@ -6815,6 +6831,7 @@ var DelegationManager = class {
|
|
|
6815
6831
|
)
|
|
6816
6832
|
};
|
|
6817
6833
|
}
|
|
6834
|
+
if (validated.data.direction === "issued") return this.queryIssued(validated.data);
|
|
6818
6835
|
if (!this.invokeAny) {
|
|
6819
6836
|
return {
|
|
6820
6837
|
ok: false,
|
|
@@ -6836,7 +6853,7 @@ var DelegationManager = class {
|
|
|
6836
6853
|
}
|
|
6837
6854
|
try {
|
|
6838
6855
|
let response;
|
|
6839
|
-
for (const spaceId of
|
|
6856
|
+
for (const spaceId of new Set(scope.spaces ?? [accountSpaceId, this.session.spaceId])) {
|
|
6840
6857
|
const headers = Object.fromEntries(new Headers(this.invokeAny(this.session, [{
|
|
6841
6858
|
spaceId,
|
|
6842
6859
|
service: "delegation",
|
|
@@ -6896,6 +6913,45 @@ var DelegationManager = class {
|
|
|
6896
6913
|
};
|
|
6897
6914
|
}
|
|
6898
6915
|
}
|
|
6916
|
+
/** The state the lazily loaded issued-delegation module reads and updates. */
|
|
6917
|
+
issuedContext() {
|
|
6918
|
+
return {
|
|
6919
|
+
host: this.host,
|
|
6920
|
+
fetchFn: this.fetchFn,
|
|
6921
|
+
sessionInvocationSigner: this.sessionInvocationSigner,
|
|
6922
|
+
state: this.issuedState
|
|
6923
|
+
};
|
|
6924
|
+
}
|
|
6925
|
+
/**
|
|
6926
|
+
* Lists the delegations this session's own key issued (`direction: "issued"`).
|
|
6927
|
+
* The implementation loads on first use.
|
|
6928
|
+
*/
|
|
6929
|
+
async queryIssued(options2) {
|
|
6930
|
+
const issued = await import("@tinycloud/sdk-core/delegations/issued");
|
|
6931
|
+
return issued.queryIssued(this.issuedContext(), options2);
|
|
6932
|
+
}
|
|
6933
|
+
/**
|
|
6934
|
+
* Every delegation this session's key issued, following `nextCursor` until
|
|
6935
|
+
* it is absent. `maxItems` stops early once that many are collected;
|
|
6936
|
+
* `untilCid` stops after the page that contains that delegation. Items come
|
|
6937
|
+
* in CID order (each carries `issuedAt`). The implementation loads on first use.
|
|
6938
|
+
*/
|
|
6939
|
+
async queryAllIssued(options2 = {}) {
|
|
6940
|
+
const issued = await import("@tinycloud/sdk-core/delegations/issued");
|
|
6941
|
+
return issued.queryAllIssued((page) => this.query(page), options2);
|
|
6942
|
+
}
|
|
6943
|
+
/**
|
|
6944
|
+
* Revokes a delegation this session's key issued, with no proof: a UCAN
|
|
6945
|
+
* signed by the session's own did:key carrying `urn:cid:<cid>` and
|
|
6946
|
+
* `tinycloud.delegation/revoke`. Revoking a delegation that is already revoked
|
|
6947
|
+
* succeeds. A refusal (the key is not the delegator) carries the node's
|
|
6948
|
+
* message; 503 means the chain is too deep right now, retry later.
|
|
6949
|
+
* The implementation loads on first use.
|
|
6950
|
+
*/
|
|
6951
|
+
async revokeIssued(cid2) {
|
|
6952
|
+
const issued = await import("@tinycloud/sdk-core/delegations/issued");
|
|
6953
|
+
return issued.revokeIssued(this.issuedContext(), cid2);
|
|
6954
|
+
}
|
|
6899
6955
|
/**
|
|
6900
6956
|
* Gets the full delegation chain for a given delegation.
|
|
6901
6957
|
*
|
|
@@ -7202,7 +7258,8 @@ function validateEncodedShareData(data) {
|
|
|
7202
7258
|
// src/manifest.ts
|
|
7203
7259
|
import ms from "ms";
|
|
7204
7260
|
import { ACCOUNT_MANIFEST_PERMISSIONS } from "@tinycloud/bootstrap";
|
|
7205
|
-
import { resolveSecretPath, SECRET_NAME_RE } from "@tinycloud/sdk-services";
|
|
7261
|
+
import { resolveSecretPath, SECRET_NAME_RE, parseNetworkId } from "@tinycloud/sdk-services";
|
|
7262
|
+
import { parseEncryptionResource as parseEncryptionResource2 } from "@tinycloud/sdk-services/encryption/resource";
|
|
7206
7263
|
var DEFAULT_KNOWLEDGE_ROOT = "knowledge/index.md";
|
|
7207
7264
|
var ManifestValidationError = class extends Error {
|
|
7208
7265
|
constructor(message) {
|
|
@@ -7325,6 +7382,14 @@ function expandEncryptionPermissionEntry(entry) {
|
|
|
7325
7382
|
`tinycloud.encryption entries require path to be a networkId URN (got ${JSON.stringify(entry.path)})`
|
|
7326
7383
|
);
|
|
7327
7384
|
}
|
|
7385
|
+
if (entry.path.includes("/")) {
|
|
7386
|
+
throw new ManifestValidationError("named keys go in manifest.keys");
|
|
7387
|
+
}
|
|
7388
|
+
try {
|
|
7389
|
+
parseNetworkId(entry.path);
|
|
7390
|
+
} catch {
|
|
7391
|
+
throw new ManifestValidationError("tinycloud.encryption entries require a valid networkId URN");
|
|
7392
|
+
}
|
|
7328
7393
|
const normalizedActions = [];
|
|
7329
7394
|
for (const action of entry.actions) {
|
|
7330
7395
|
if (action === "decrypt" || action === "tinycloud.encryption/decrypt") {
|
|
@@ -7371,6 +7436,29 @@ function expandEncryptionPermissionEntry(entry) {
|
|
|
7371
7436
|
function expandPermissionEntries(entries) {
|
|
7372
7437
|
return entries.flatMap(expandPermissionEntry);
|
|
7373
7438
|
}
|
|
7439
|
+
function expandDelegationPermissionEntries(entries) {
|
|
7440
|
+
return entries.flatMap((entry) => {
|
|
7441
|
+
if (entry.service !== ENCRYPTION_PERMISSION_SERVICE || typeof entry.path !== "string" || !entry.path.includes("/")) {
|
|
7442
|
+
return expandPermissionEntry(entry);
|
|
7443
|
+
}
|
|
7444
|
+
let resource;
|
|
7445
|
+
try {
|
|
7446
|
+
resource = parseEncryptionResource2(entry.path);
|
|
7447
|
+
} catch {
|
|
7448
|
+
throw new ManifestValidationError("delegation requires a canonical exact named-key resource");
|
|
7449
|
+
}
|
|
7450
|
+
if (resource.kind !== "key" || !Array.isArray(entry.actions) || entry.actions.length === 0 || entry.actions.some((action) => action !== "decrypt" && action !== "tinycloud.encryption/decrypt")) {
|
|
7451
|
+
throw new ManifestValidationError("named-key delegation requires an exact key and decrypt only");
|
|
7452
|
+
}
|
|
7453
|
+
return [{
|
|
7454
|
+
...entry,
|
|
7455
|
+
space: ENCRYPTION_MANIFEST_SPACE,
|
|
7456
|
+
path: `${resource.networkId}/${resource.path}`,
|
|
7457
|
+
actions: ["tinycloud.encryption/decrypt"],
|
|
7458
|
+
skipPrefix: true
|
|
7459
|
+
}];
|
|
7460
|
+
});
|
|
7461
|
+
}
|
|
7374
7462
|
function applyPrefix(prefix, path, skipPrefix) {
|
|
7375
7463
|
if (skipPrefix) {
|
|
7376
7464
|
return path;
|
|
@@ -7449,8 +7537,56 @@ function validateManifest(input) {
|
|
|
7449
7537
|
if (m.secrets !== void 0) {
|
|
7450
7538
|
validateManifestSecrets(m.secrets);
|
|
7451
7539
|
}
|
|
7540
|
+
if (Object.prototype.hasOwnProperty.call(m, "keys_app_id") && (typeof m.keys_app_id !== "string" || !KEY_APP_ID.test(m.keys_app_id))) {
|
|
7541
|
+
throw new ManifestValidationError("manifest.keys_app_id is invalid");
|
|
7542
|
+
}
|
|
7543
|
+
validateManifestKeys(m.keys_app_id ?? m.app_id, m.keys);
|
|
7452
7544
|
return m;
|
|
7453
7545
|
}
|
|
7546
|
+
var KEY_SEGMENT = "[a-z0-9][a-z0-9._-]{0,63}";
|
|
7547
|
+
var KEY_APP_ID = new RegExp(`^${KEY_SEGMENT}$`);
|
|
7548
|
+
var KEY_PATH = new RegExp(`^${KEY_SEGMENT}(?:/${KEY_SEGMENT}){2,7}$`);
|
|
7549
|
+
var KEY_ABILITIES = ["key.create", "decrypt", "key.block", "key.unblock"];
|
|
7550
|
+
function validateManifestKeys(appId, keys) {
|
|
7551
|
+
if (keys === void 0) return;
|
|
7552
|
+
if (!Array.isArray(keys) || keys.length > 32) {
|
|
7553
|
+
throw new ManifestValidationError("manifest.keys must contain at most 32 entries");
|
|
7554
|
+
}
|
|
7555
|
+
if (keys.length === 0) return;
|
|
7556
|
+
if (!KEY_APP_ID.test(appId)) {
|
|
7557
|
+
throw new ManifestValidationError("manifest.keys requires a valid app_id");
|
|
7558
|
+
}
|
|
7559
|
+
const paths = /* @__PURE__ */ new Set();
|
|
7560
|
+
let prefixSeen = false;
|
|
7561
|
+
for (const [index, value] of keys.entries()) {
|
|
7562
|
+
const label = `manifest.keys[${index}]`;
|
|
7563
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
7564
|
+
throw new ManifestValidationError(`${label} must be an object`);
|
|
7565
|
+
}
|
|
7566
|
+
const entry = value;
|
|
7567
|
+
const fields = Object.keys(entry);
|
|
7568
|
+
const hasPath = Object.prototype.hasOwnProperty.call(entry, "path");
|
|
7569
|
+
const hasPrefix = Object.prototype.hasOwnProperty.call(entry, "prefix");
|
|
7570
|
+
if (hasPath === hasPrefix || fields.some((field) => !["path", "prefix", "abilities"].includes(field))) {
|
|
7571
|
+
throw new ManifestValidationError(`${label} must have one path or prefix and abilities`);
|
|
7572
|
+
}
|
|
7573
|
+
if (!Array.isArray(entry.abilities) || entry.abilities.length === 0 || new Set(entry.abilities).size !== entry.abilities.length || entry.abilities.some((ability) => typeof ability !== "string" || !KEY_ABILITIES.includes(ability))) {
|
|
7574
|
+
throw new ManifestValidationError(`${label}.abilities is invalid`);
|
|
7575
|
+
}
|
|
7576
|
+
if (hasPath) {
|
|
7577
|
+
const path = entry.path;
|
|
7578
|
+
if (typeof path !== "string" || path.length > 255 || !KEY_PATH.test(path) || !path.startsWith(`apps/${appId}/`) || paths.has(path)) {
|
|
7579
|
+
throw new ManifestValidationError(`${label}.path is invalid`);
|
|
7580
|
+
}
|
|
7581
|
+
paths.add(path);
|
|
7582
|
+
} else {
|
|
7583
|
+
if (entry.prefix !== `apps/${appId}/` || prefixSeen) {
|
|
7584
|
+
throw new ManifestValidationError(`${label}.prefix is invalid`);
|
|
7585
|
+
}
|
|
7586
|
+
prefixSeen = true;
|
|
7587
|
+
}
|
|
7588
|
+
}
|
|
7589
|
+
}
|
|
7454
7590
|
function resolveManifestKnowledgeRoot(knowledge) {
|
|
7455
7591
|
if (knowledge === void 0) {
|
|
7456
7592
|
return void 0;
|
|
@@ -7968,6 +8104,7 @@ function manifestAbilitiesUnion(resolved) {
|
|
|
7968
8104
|
}
|
|
7969
8105
|
|
|
7970
8106
|
// src/capabilities.ts
|
|
8107
|
+
import { encryptionResourceContains } from "@tinycloud/sdk-services/encryption/resource";
|
|
7971
8108
|
var PermissionNotInManifestError = class extends Error {
|
|
7972
8109
|
constructor(missing, granted) {
|
|
7973
8110
|
super(
|
|
@@ -8019,10 +8156,9 @@ function isCapabilitySubset(requested, granted) {
|
|
|
8019
8156
|
const grantedActions = matchingScope.flatMap(
|
|
8020
8157
|
(entry) => expandActionShortNames(entry.service, entry.actions)
|
|
8021
8158
|
);
|
|
8159
|
+
const namedKeyRequest = req.service === "tinycloud.encryption" && req.path.startsWith("urn:tinycloud:encryption:") && req.path.includes("/");
|
|
8022
8160
|
const covered = requestedActions.every(
|
|
8023
|
-
(requestedAction) => grantedActions.some(
|
|
8024
|
-
(grantedAction) => actionContains(grantedAction, requestedAction)
|
|
8025
|
-
)
|
|
8161
|
+
(requestedAction) => grantedActions.some((grantedAction) => namedKeyRequest ? grantedAction === requestedAction : actionContains(grantedAction, requestedAction))
|
|
8026
8162
|
);
|
|
8027
8163
|
if (!covered) {
|
|
8028
8164
|
missing.push(cloneEntry(req));
|
|
@@ -8037,7 +8173,9 @@ function canonicalizeEntryScopeMatches(requested, granted) {
|
|
|
8037
8173
|
if (normalizeSpace(requested.space ?? DEFAULT_MANIFEST_SPACE) !== normalizeSpace(granted.space ?? DEFAULT_MANIFEST_SPACE)) {
|
|
8038
8174
|
return false;
|
|
8039
8175
|
}
|
|
8040
|
-
|
|
8176
|
+
const namedKeyRequest = requested.service === "tinycloud.encryption" && requested.path.startsWith("urn:tinycloud:encryption:") && requested.path.includes("/");
|
|
8177
|
+
const coveredPath = namedKeyRequest ? encryptionResourceContains(granted.path, requested.path) : pathContains2(granted.path, requested.path);
|
|
8178
|
+
if (!coveredPath) {
|
|
8041
8179
|
return false;
|
|
8042
8180
|
}
|
|
8043
8181
|
if (!recapCaveatsEqual(requested.caveats, granted.caveats)) {
|
|
@@ -12242,15 +12380,15 @@ function markDelegationTerminal(token, key) {
|
|
|
12242
12380
|
// src/networkId.ts
|
|
12243
12381
|
import {
|
|
12244
12382
|
buildNetworkId,
|
|
12245
|
-
parseNetworkId
|
|
12383
|
+
parseNetworkId as parseNetworkId2
|
|
12246
12384
|
} from "@tinycloud/sdk-services";
|
|
12247
12385
|
function canonicalizeNetworkId(networkId) {
|
|
12248
|
-
const parsed =
|
|
12386
|
+
const parsed = parseNetworkId2(networkId);
|
|
12249
12387
|
return buildNetworkId(canonicalizeDid(parsed.ownerDid), parsed.name);
|
|
12250
12388
|
}
|
|
12251
12389
|
function parseCanonicalNetworkId(networkId) {
|
|
12252
12390
|
const canonical = canonicalizeNetworkId(networkId);
|
|
12253
|
-
return
|
|
12391
|
+
return parseNetworkId2(canonical);
|
|
12254
12392
|
}
|
|
12255
12393
|
|
|
12256
12394
|
// src/storage.schema.ts
|
|
@@ -15063,7 +15201,7 @@ import {
|
|
|
15063
15201
|
resolveSecretPath as resolveSecretPath2,
|
|
15064
15202
|
EncryptionService,
|
|
15065
15203
|
DecryptTransportResponseError,
|
|
15066
|
-
parseNetworkId as
|
|
15204
|
+
parseNetworkId as parseNetworkId3,
|
|
15067
15205
|
buildNetworkId as buildNetworkId2,
|
|
15068
15206
|
isNetworkId,
|
|
15069
15207
|
networkDiscoveryKey,
|
|
@@ -19833,6 +19971,7 @@ export {
|
|
|
19833
19971
|
ensureNetworkUsableForDecrypt,
|
|
19834
19972
|
err5 as err,
|
|
19835
19973
|
expandActionShortNames,
|
|
19974
|
+
expandDelegationPermissionEntries,
|
|
19836
19975
|
expandPermissionEntries,
|
|
19837
19976
|
expandPermissionEntry,
|
|
19838
19977
|
extractImmutableSiweFields,
|
|
@@ -19888,7 +20027,7 @@ export {
|
|
|
19888
20027
|
parseCapabilityResource,
|
|
19889
20028
|
parseCompactUcanAuthorization,
|
|
19890
20029
|
parseExpiry,
|
|
19891
|
-
|
|
20030
|
+
parseNetworkId3 as parseNetworkId,
|
|
19892
20031
|
parseNodeNativePortableDelegation,
|
|
19893
20032
|
parsePkhDid,
|
|
19894
20033
|
parsePolicySessionUcan,
|