@domino-sdk/relay 0.13.0 → 0.14.0

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/index.js CHANGED
@@ -51,7 +51,7 @@ import {
51
51
  walletChallengeSchema,
52
52
  walletConnectionSchema,
53
53
  walletProofSchema
54
- } from "./chunk-Y7FVHF65.js";
54
+ } from "./chunk-YZJOZL4V.js";
55
55
  import {
56
56
  customSource,
57
57
  defineLeaderboard,
@@ -90,6 +90,9 @@ import {
90
90
  trackerIngestResultSchema,
91
91
  trackerIngestSchema,
92
92
  trackerPageSchema,
93
+ trackerPreviewInputSchema,
94
+ trackerPreviewLimits,
95
+ trackerPreviewSchema,
93
96
  trackerQuerySchema,
94
97
  trackerRecordInputSchema,
95
98
  trackerRecordSchema,
@@ -98,14 +101,14 @@ import {
98
101
  trackerSummarySchema,
99
102
  x,
100
103
  xTweetSchema
101
- } from "./chunk-XOEREIXI.js";
104
+ } from "./chunk-CJARNIVP.js";
102
105
  import {
103
106
  authScopeSchema,
104
107
  exchangeResultSchema,
105
108
  exchangeSchema,
106
109
  principalSchema,
107
110
  sessionSchema
108
- } from "./chunk-VDWQL6HV.js";
111
+ } from "./chunk-I2CCMGN6.js";
109
112
  import {
110
113
  accountConnectionSchema,
111
114
  artifactSchema,
@@ -197,7 +200,7 @@ import {
197
200
  tierRewardSchema,
198
201
  tieredRewardDefinitionSchema,
199
202
  triggerSchema
200
- } from "./chunk-3UOTI4IU.js";
203
+ } from "./chunk-FUQYKTOJ.js";
201
204
  export {
202
205
  QuestUpdatedError,
203
206
  RelayError,
@@ -378,6 +381,9 @@ export {
378
381
  trackerIngestResultSchema,
379
382
  trackerIngestSchema,
380
383
  trackerPageSchema,
384
+ trackerPreviewInputSchema,
385
+ trackerPreviewLimits,
386
+ trackerPreviewSchema,
381
387
  trackerQuerySchema,
382
388
  trackerRecordInputSchema,
383
389
  trackerRecordSchema,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  exchangeResultSchema
3
- } from "./chunk-VDWQL6HV.js";
4
- import "./chunk-3UOTI4IU.js";
3
+ } from "./chunk-I2CCMGN6.js";
4
+ import "./chunk-FUQYKTOJ.js";
5
5
 
6
6
  // src/portal-proxy.ts
7
7
  async function proxyParticipant(request, upstream) {
@@ -12,14 +12,16 @@ async function proxyParticipant(request, upstream) {
12
12
  ) || /^\/v1\/me\/(progress|experience|attempts|evidence|pass|collection-pass|wallet(?:\/challenge|\/verify)?)$/.test(
13
13
  path
14
14
  ) || /^\/v1\/attempts\/[\w-]+(?:\/retry)?$/.test(path) || /^\/v1\/referrals(?:\/(me|history))?$/.test(path) && request.method === "GET" || path === "/v1/reward-deliveries" && request.method === "GET" || path === "/v1/referrals/accept" && request.method === "POST" || request.method === "GET" && /^\/v1\/leaderboards(?:\/[\w-]+)?$/.test(path));
15
- if (!allowed || !["GET", "POST"].includes(request.method))
15
+ if (!allowed || !["GET", "POST"].includes(request.method)) {
16
16
  return Response.json({ error: "Route not found" }, { status: 404 });
17
+ }
17
18
  const target = new URL(upstream.baseUrl);
18
- if (target.protocol !== "https:" || target.username || target.password || target.pathname !== "/" || target.search || target.hash)
19
+ if (target.protocol !== "https:" || target.username || target.password || target.pathname !== "/" || target.search || target.hash) {
19
20
  return Response.json(
20
21
  { error: "Participant API is not configured" },
21
22
  { status: 503 }
22
23
  );
24
+ }
23
25
  target.pathname = path;
24
26
  target.search = incoming.search;
25
27
  const headers = new Headers();
@@ -33,10 +35,13 @@ async function proxyParticipant(request, upstream) {
33
35
  "CF-Connecting-IP"
34
36
  ]) {
35
37
  const value = request.headers.get(name);
36
- if (value) headers.set(name, value);
38
+ if (value) {
39
+ headers.set(name, value);
40
+ }
37
41
  }
38
- if (upstream.hosted ?? true)
42
+ if (upstream.hosted ?? true) {
39
43
  headers.set("X-Relay-App-Origin", incoming.origin);
44
+ }
40
45
  const send = upstream.fetch;
41
46
  const response = await send(target, {
42
47
  method: request.method,
@@ -54,14 +59,16 @@ async function proxyParticipant(request, upstream) {
54
59
  function createParticipantBackend(options) {
55
60
  const origin = new URL(options.origin);
56
61
  const base = new URL(options.baseUrl);
57
- if (origin.protocol !== "https:" || origin.origin !== options.origin || base.protocol !== "https:" || base.origin !== options.baseUrl)
62
+ if (origin.protocol !== "https:" || origin.origin !== options.origin || base.protocol !== "https:" || base.origin !== options.baseUrl) {
58
63
  throw new Error(
59
64
  "Configure exact HTTPS application and participant API origins"
60
65
  );
61
- if (!options.integrationKey.startsWith("di_"))
66
+ }
67
+ if (!options.integrationKey.startsWith("di_")) {
62
68
  throw new Error(
63
69
  "Use an identity integration server key from Console Settings"
64
70
  );
71
+ }
65
72
  const send = options.fetch ?? fetch;
66
73
  const cookie = (token) => `domino_app_session=${token}; Path=/relay; HttpOnly; Secure; SameSite=Lax; Max-Age=${token ? 28800 : 0}`;
67
74
  return async (request) => {
@@ -70,12 +77,14 @@ function createParticipantBackend(options) {
70
77
  { error },
71
78
  { status, headers: { "Cache-Control": "no-store" } }
72
79
  );
73
- if (url.origin !== options.origin)
80
+ if (url.origin !== options.origin) {
74
81
  return reject("Unexpected application origin", 403);
75
- if (request.method !== "GET" && (request.headers.get("Origin") !== options.origin || request.headers.get("X-Relay-CSRF") !== "1"))
82
+ }
83
+ if (request.method !== "GET" && (request.headers.get("Origin") !== options.origin || request.headers.get("X-Relay-CSRF") !== "1")) {
76
84
  return reject("Untrusted browser request", 403);
85
+ }
77
86
  const identity = await options.authenticate(request);
78
- if (!identity)
87
+ if (!identity) {
79
88
  return Response.json(
80
89
  url.pathname === "/relay/v1/auth/session" ? null : { error: "Sign in to the application" },
81
90
  {
@@ -83,6 +92,7 @@ function createParticipantBackend(options) {
83
92
  headers: { "Set-Cookie": cookie(""), "Cache-Control": "no-store" }
84
93
  }
85
94
  );
95
+ }
86
96
  const previousToken = request.headers.get("Cookie")?.split(";").map((value) => value.trim()).find((value) => value.startsWith("domino_app_session="))?.slice("domino_app_session=".length);
87
97
  const exchanged = await send(options.baseUrl + "/v1/auth/exchange", {
88
98
  method: "POST",
@@ -93,11 +103,12 @@ function createParticipantBackend(options) {
93
103
  },
94
104
  body: JSON.stringify({ subject: identity.subject, previousToken })
95
105
  });
96
- if (!exchanged.ok)
106
+ if (!exchanged.ok) {
97
107
  return reject(
98
108
  "Domino identity exchange failed. Check the server integration key and its expiry in Console Settings.",
99
109
  exchanged.status
100
110
  );
111
+ }
101
112
  const { token } = exchangeResultSchema.parse(await exchanged.json());
102
113
  const response = await proxyParticipant(request, {
103
114
  baseUrl: options.baseUrl,
@@ -105,7 +116,9 @@ function createParticipantBackend(options) {
105
116
  const headers2 = new Headers(init?.headers);
106
117
  headers2.delete("Cookie");
107
118
  const returnBinding = request.headers.get("Cookie")?.split(";").map((value) => value.trim()).find((value) => value.startsWith("relay_auth_return="));
108
- if (returnBinding) headers2.set("Cookie", returnBinding);
119
+ if (returnBinding) {
120
+ headers2.set("Cookie", returnBinding);
121
+ }
109
122
  headers2.set("Authorization", "Bearer " + token);
110
123
  headers2.set("X-Relay-Integration-Key", options.integrationKey);
111
124
  headers2.set("Origin", options.origin);
package/dist/schema.d.ts CHANGED
@@ -83,8 +83,8 @@ declare const triggerSchema: z.ZodUnion<readonly [z.ZodObject<{
83
83
  none: "none";
84
84
  }>;
85
85
  actor: z.ZodOptional<z.ZodEnum<{
86
- member: "member";
87
86
  staff: "staff";
87
+ member: "member";
88
88
  }>>;
89
89
  }, z.core.$strict>, z.ZodObject<{
90
90
  kind: z.ZodLiteral<"automatic">;
@@ -373,8 +373,8 @@ declare const questDescriptionSchema: z.ZodObject<{
373
373
  none: "none";
374
374
  }>;
375
375
  actor: z.ZodOptional<z.ZodEnum<{
376
- member: "member";
377
376
  staff: "staff";
377
+ member: "member";
378
378
  }>>;
379
379
  }, z.core.$strict>, z.ZodObject<{
380
380
  kind: z.ZodLiteral<"automatic">;
@@ -614,8 +614,8 @@ declare const publishedReleaseSchema: z.ZodUnion<readonly [z.ZodObject<{
614
614
  none: "none";
615
615
  }>;
616
616
  actor: z.ZodOptional<z.ZodEnum<{
617
- member: "member";
618
617
  staff: "staff";
618
+ member: "member";
619
619
  }>>;
620
620
  }, z.core.$strict>, z.ZodObject<{
621
621
  kind: z.ZodLiteral<"automatic">;
@@ -672,8 +672,8 @@ declare const publishedReleaseSchema: z.ZodUnion<readonly [z.ZodObject<{
672
672
  typeVersion: z.ZodString;
673
673
  }, z.core.$strict>>;
674
674
  lifecycle: z.ZodDefault<z.ZodEnum<{
675
- active: "active";
676
675
  paused: "paused";
676
+ active: "active";
677
677
  archived: "archived";
678
678
  }>>;
679
679
  configuration: z.ZodOptional<z.ZodObject<{
@@ -871,7 +871,7 @@ declare const publishedReleaseSchema: z.ZodUnion<readonly [z.ZodObject<{
871
871
  trigger: {
872
872
  kind: "manual";
873
873
  input: "photo" | "quiz" | "none";
874
- actor?: "member" | "staff" | undefined;
874
+ actor?: "staff" | "member" | undefined;
875
875
  } | {
876
876
  kind: "automatic";
877
877
  observation?: {
@@ -907,7 +907,7 @@ declare const publishedReleaseSchema: z.ZodUnion<readonly [z.ZodObject<{
907
907
  id: string;
908
908
  createdAt: number;
909
909
  legacy: boolean;
910
- lifecycle: "active" | "paused" | "archived";
910
+ lifecycle: "paused" | "active" | "archived";
911
911
  key?: string | undefined;
912
912
  origin?: {
913
913
  typeVersion: string;
@@ -1115,8 +1115,8 @@ declare const templateSchema: z.ZodObject<{
1115
1115
  none: "none";
1116
1116
  }>;
1117
1117
  actor: z.ZodOptional<z.ZodEnum<{
1118
- member: "member";
1119
1118
  staff: "staff";
1119
+ member: "member";
1120
1120
  }>>;
1121
1121
  }, z.core.$strict>, z.ZodObject<{
1122
1122
  kind: z.ZodLiteral<"automatic">;
@@ -1499,8 +1499,8 @@ declare const memberProgressSchema: z.ZodObject<{
1499
1499
  none: "none";
1500
1500
  }>;
1501
1501
  actor: z.ZodOptional<z.ZodEnum<{
1502
- member: "member";
1503
1502
  staff: "staff";
1503
+ member: "member";
1504
1504
  }>>;
1505
1505
  }, z.core.$strict>, z.ZodObject<{
1506
1506
  kind: z.ZodLiteral<"automatic">;
@@ -1868,8 +1868,8 @@ declare const snapshotSchema: z.ZodObject<{
1868
1868
  none: "none";
1869
1869
  }>;
1870
1870
  actor: z.ZodOptional<z.ZodEnum<{
1871
- member: "member";
1872
1871
  staff: "staff";
1872
+ member: "member";
1873
1873
  }>>;
1874
1874
  }, z.core.$strict>, z.ZodObject<{
1875
1875
  kind: z.ZodLiteral<"automatic">;
@@ -1926,8 +1926,8 @@ declare const snapshotSchema: z.ZodObject<{
1926
1926
  typeVersion: z.ZodString;
1927
1927
  }, z.core.$strict>>;
1928
1928
  lifecycle: z.ZodDefault<z.ZodEnum<{
1929
- active: "active";
1930
1929
  paused: "paused";
1930
+ active: "active";
1931
1931
  archived: "archived";
1932
1932
  }>>;
1933
1933
  configuration: z.ZodOptional<z.ZodObject<{
@@ -2125,7 +2125,7 @@ declare const snapshotSchema: z.ZodObject<{
2125
2125
  trigger: {
2126
2126
  kind: "manual";
2127
2127
  input: "photo" | "quiz" | "none";
2128
- actor?: "member" | "staff" | undefined;
2128
+ actor?: "staff" | "member" | undefined;
2129
2129
  } | {
2130
2130
  kind: "automatic";
2131
2131
  observation?: {
@@ -2161,7 +2161,7 @@ declare const snapshotSchema: z.ZodObject<{
2161
2161
  id: string;
2162
2162
  createdAt: number;
2163
2163
  legacy: boolean;
2164
- lifecycle: "active" | "paused" | "archived";
2164
+ lifecycle: "paused" | "active" | "archived";
2165
2165
  key?: string | undefined;
2166
2166
  origin?: {
2167
2167
  typeVersion: string;
package/dist/schema.js CHANGED
@@ -52,7 +52,7 @@ import {
52
52
  tierRewardSchema,
53
53
  tieredRewardDefinitionSchema,
54
54
  triggerSchema
55
- } from "./chunk-3UOTI4IU.js";
55
+ } from "./chunk-FUQYKTOJ.js";
56
56
  export {
57
57
  artifactSchema,
58
58
  attemptSchema,
@@ -330,6 +330,47 @@ declare const trackerSummarySchema: z.ZodObject<{
330
330
  records: z.ZodNumber;
331
331
  }, z.core.$strip>;
332
332
  }, z.core.$strip>;
333
+ /** Preview bounds: one sample, never stored, never a tracker. */
334
+ declare const trackerPreviewLimits: {
335
+ readonly pages: 3;
336
+ readonly posts: 100;
337
+ readonly days: 30;
338
+ };
339
+ /** A bounded X search sample for exploring a query before defining a tracker in code. */
340
+ declare const trackerPreviewInputSchema: z.ZodObject<{
341
+ query: z.ZodString;
342
+ since: z.ZodNumber;
343
+ }, z.core.$strict>;
344
+ type TrackerPreviewInput = z.input<typeof trackerPreviewInputSchema>;
345
+ declare const trackerPreviewSchema: z.ZodObject<{
346
+ query: z.ZodString;
347
+ since: z.ZodNumber;
348
+ fetchedAt: z.ZodNumber;
349
+ pages: z.ZodNumber;
350
+ posts: z.ZodArray<z.ZodObject<{
351
+ id: z.ZodString;
352
+ userId: z.ZodString;
353
+ content: z.ZodString;
354
+ createdAt: z.ZodISODateTime;
355
+ author: z.ZodObject<{
356
+ id: z.ZodString;
357
+ username: z.ZodString;
358
+ name: z.ZodString;
359
+ verified: z.ZodDefault<z.ZodNullable<z.ZodBoolean>>;
360
+ }, z.core.$loose>;
361
+ likeCount: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
362
+ replyCount: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
363
+ retweetCount: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
364
+ quoteCount: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
365
+ viewCount: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
366
+ bookmarkCount: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
367
+ isReply: z.ZodBoolean;
368
+ isRetweet: z.ZodBoolean;
369
+ isQuoteTweet: z.ZodBoolean;
370
+ }, z.core.$loose>>;
371
+ more: z.ZodBoolean;
372
+ }, z.core.$strip>;
373
+ type TrackerPreview = z.infer<typeof trackerPreviewSchema>;
333
374
  /** Read-only capabilities in hosted quest evaluation; results are captured for replay. */
334
375
  type TrackerReader = {
335
376
  records(reference: string | {
@@ -2724,4 +2765,4 @@ declare function parseSettings(fields: Record<string, SettingField>, input: unkn
2724
2765
  declare function missingResourceSettings(fields: Record<string, SettingField>, values: SettingsValues): string[];
2725
2766
  declare function compatibleSettingKinds(previous: SettingField, next: SettingField): boolean;
2726
2767
 
2727
- export { discordServerSchema as $, type AuthoringIdentity as A, type Budget as B, type TrackerRecord as C, type DiscordMember as D, type TrackerStatus as E, accountConnectionSchema as F, authProviderSchema as G, budgetSchema as H, type IntegrationProvider as I, communityTimeZoneSchema as J, compatibleSettingKinds as K, type LeaderboardQuery as L, completionKeySchema as M, completionPolicySchema as N, type Observation as O, type ProjectIntegration as P, type QuizContent as Q, type ReferralDefinition as R, type SolanaBalance as S, type TrackerReader as T, discordMemberInputSchema as U, discordMemberSchema as V, type WalletConnection as W, type XTweet as X, discordMessageInputSchema as Y, discordMessageSchema as Z, discordReactionSchema as _, type DiscordMessage as a, integrationProviderSchema as a0, leaderboardArtifactSchema as a1, leaderboardAuthoringSchema as a2, leaderboardDefinitionSchema as a3, leaderboardExclusionSchema as a4, leaderboardGroupSchema as a5, leaderboardPageSchema as a6, leaderboardPreviewSchema as a7, leaderboardQuerySchema as a8, leaderboardRowSchema as a9, referralSummarySchema as aA, resourceOptionsSchema as aB, resourceRequestSchema as aC, resourceSourceSchema as aD, settingFieldSchema as aE, settingsFieldsSchema as aF, settingsValuesSchema as aG, solanaAddressSchema as aH, solanaBalanceSchema as aI, streakPolicySchema as aJ, streakProgressSchema as aK, trackerArtifactSchema as aL, trackerControlSchema as aM, trackerDefinitionSchema as aN, trackerIngestResultSchema as aO, trackerIngestSchema as aP, trackerPageSchema as aQ, trackerQuerySchema as aR, trackerRecordInputSchema as aS, trackerRecordSchema as aT, trackerSourceSchema as aU, trackerStatusSchema as aV, trackerSummarySchema as aW, walletChallengeSchema as aX, walletConnectionSchema as aY, xTweetSchema as aZ, leaderboardWindowSchema as aa, missingResourceSettings as ab, observationInputSchema as ac, observationReceiptSchema as ad, observationSchema as ae, parseSettings as af, pointsAccountSchema as ag, pointsEntrySchema as ah, pointsMutationSchema as ai, pointsResultSchema as aj, projectIntegrationSchema as ak, publishedLeaderboardSchema as al, publishedReferralSchema as am, publishedTrackerSchema as an, quizChoiceSchema as ao, quizContentSchema as ap, quizQuestionSchema as aq, quizQuestionsSchema as ar, referralAccountSchema as as, referralArtifactSchema as at, referralAuthoringSchema as au, referralControlSchema as av, referralDefinitionSchema as aw, referralHistorySchema as ax, referralManagementSchema as ay, referralRelationshipSchema as az, type StreakPolicy as b, customSource as c, defineLeaderboard as d, defineReferral as e, defineTracker as f, discord as g, streak as h, walletProofSchema as i, type AuthProvider as j, type AccountConnection as k, type LeaderboardDefinition as l, type LeaderboardPage as m, type ResourceOption as n, observationTriggerSchema as o, pointLedgers as p, type ResourceSource as q, referralAcceptSchema as r, settings as s, type SettingField as t, type SettingsValues as u, type StreakProgress as v, walletChallengeInputSchema as w, x, type TrackerDefinition as y, type TrackerQuery as z };
2768
+ export { discordMessageSchema as $, type AuthoringIdentity as A, type Budget as B, type TrackerPreviewInput as C, type DiscordMember as D, type TrackerQuery as E, type TrackerRecord as F, type TrackerStatus as G, accountConnectionSchema as H, type IntegrationProvider as I, authProviderSchema as J, budgetSchema as K, type LeaderboardQuery as L, communityTimeZoneSchema as M, compatibleSettingKinds as N, type Observation as O, type ProjectIntegration as P, type QuizContent as Q, type ReferralDefinition as R, type SolanaBalance as S, type TrackerReader as T, completionKeySchema as U, completionPolicySchema as V, type WalletConnection as W, type XTweet as X, discordMemberInputSchema as Y, discordMemberSchema as Z, discordMessageInputSchema as _, type DiscordMessage as a, trackerSummarySchema as a$, discordReactionSchema as a0, discordServerSchema as a1, integrationProviderSchema as a2, leaderboardArtifactSchema as a3, leaderboardAuthoringSchema as a4, leaderboardDefinitionSchema as a5, leaderboardExclusionSchema as a6, leaderboardGroupSchema as a7, leaderboardPageSchema as a8, leaderboardPreviewSchema as a9, referralManagementSchema as aA, referralRelationshipSchema as aB, referralSummarySchema as aC, resourceOptionsSchema as aD, resourceRequestSchema as aE, resourceSourceSchema as aF, settingFieldSchema as aG, settingsFieldsSchema as aH, settingsValuesSchema as aI, solanaAddressSchema as aJ, solanaBalanceSchema as aK, streakPolicySchema as aL, streakProgressSchema as aM, trackerArtifactSchema as aN, trackerControlSchema as aO, trackerDefinitionSchema as aP, trackerIngestResultSchema as aQ, trackerIngestSchema as aR, trackerPageSchema as aS, trackerPreviewInputSchema as aT, trackerPreviewLimits as aU, trackerPreviewSchema as aV, trackerQuerySchema as aW, trackerRecordInputSchema as aX, trackerRecordSchema as aY, trackerSourceSchema as aZ, trackerStatusSchema as a_, leaderboardQuerySchema as aa, leaderboardRowSchema as ab, leaderboardWindowSchema as ac, missingResourceSettings as ad, observationInputSchema as ae, observationReceiptSchema as af, observationSchema as ag, parseSettings as ah, pointsAccountSchema as ai, pointsEntrySchema as aj, pointsMutationSchema as ak, pointsResultSchema as al, projectIntegrationSchema as am, publishedLeaderboardSchema as an, publishedReferralSchema as ao, publishedTrackerSchema as ap, quizChoiceSchema as aq, quizContentSchema as ar, quizQuestionSchema as as, quizQuestionsSchema as at, referralAccountSchema as au, referralArtifactSchema as av, referralAuthoringSchema as aw, referralControlSchema as ax, referralDefinitionSchema as ay, referralHistorySchema as az, type StreakPolicy as b, walletChallengeSchema as b0, walletConnectionSchema as b1, xTweetSchema as b2, customSource as c, defineLeaderboard as d, defineReferral as e, defineTracker as f, discord as g, streak as h, walletProofSchema as i, type AuthProvider as j, type AccountConnection as k, type LeaderboardDefinition as l, type LeaderboardPage as m, type ResourceOption as n, observationTriggerSchema as o, pointLedgers as p, type ResourceSource as q, referralAcceptSchema as r, settings as s, type SettingField as t, type SettingsValues as u, type StreakProgress as v, walletChallengeInputSchema as w, x, type TrackerDefinition as y, type TrackerPreview as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@domino-sdk/relay",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {