@scalemule/news-publication 0.0.47 → 0.0.49
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 +28 -0
- package/dist/{chunk-GB36YM56.js → chunk-2MEYHM7M.js} +11 -1
- package/dist/chunk-4JRWZXAP.js +62 -0
- package/dist/chunk-MBBPEFDA.js +124 -0
- package/dist/chunk-TTLM7YOH.js +444 -0
- package/dist/chunk-ZXH3O537.js +251 -0
- package/dist/community-maps-view.d.cts +3 -3
- package/dist/community-maps-view.d.ts +3 -3
- package/dist/cookie-consent-banner.d.cts +2 -2
- package/dist/cookie-consent-banner.d.ts +2 -2
- package/dist/cookie-settings-view.d.cts +2 -2
- package/dist/cookie-settings-view.d.ts +2 -2
- package/dist/index.cjs +74 -3
- package/dist/index.d.cts +12 -4
- package/dist/index.d.ts +12 -4
- package/dist/index.js +49 -6
- package/dist/marketplace-forms.d.cts +3 -3
- package/dist/marketplace-forms.d.ts +3 -3
- package/dist/marketplace-view.d.cts +5 -5
- package/dist/marketplace-view.d.ts +5 -5
- package/dist/{meet-RWmfj3I3.d.cts → meet-BWpxY20X.d.ts} +5 -0
- package/dist/{meet-RWmfj3I3.d.ts → meet-CO9Unz5M.d.cts} +5 -0
- package/dist/meet-forms.d.cts +7 -6
- package/dist/meet-forms.d.ts +7 -6
- package/dist/meet-forms.js +2 -1
- package/dist/meet-view.cjs +22 -2
- package/dist/meet-view.d.cts +7 -5
- package/dist/meet-view.d.ts +7 -5
- package/dist/meet-view.js +21 -4
- package/dist/network-bar.d.cts +2 -2
- package/dist/network-bar.d.ts +2 -2
- package/dist/network-copyright.d.cts +2 -2
- package/dist/network-copyright.d.ts +2 -2
- package/dist/pets-auth-server.cjs +212 -0
- package/dist/pets-auth-server.d.cts +17 -0
- package/dist/pets-auth-server.d.ts +17 -0
- package/dist/pets-auth-server.js +155 -0
- package/dist/pets-forms.cjs +762 -0
- package/dist/pets-forms.d.cts +26 -0
- package/dist/pets-forms.d.ts +26 -0
- package/dist/pets-forms.js +712 -0
- package/dist/pets-interactions.cjs +332 -0
- package/dist/pets-interactions.d.cts +20 -0
- package/dist/pets-interactions.d.ts +20 -0
- package/dist/pets-interactions.js +319 -0
- package/dist/pets-publication.cjs +818 -0
- package/dist/pets-publication.d.cts +36 -0
- package/dist/pets-publication.d.ts +36 -0
- package/dist/pets-publication.js +177 -0
- package/dist/pets-server.cjs +289 -0
- package/dist/pets-server.d.cts +42 -0
- package/dist/pets-server.d.ts +42 -0
- package/dist/pets-server.js +2 -0
- package/dist/pets-view.cjs +541 -0
- package/dist/pets-view.d.cts +56 -0
- package/dist/pets-view.d.ts +56 -0
- package/dist/pets-view.js +4 -0
- package/dist/pets.cjs +138 -0
- package/dist/pets.d.cts +127 -0
- package/dist/pets.d.ts +127 -0
- package/dist/pets.js +1 -0
- package/dist/sponsorship-view.d.cts +2 -2
- package/dist/sponsorship-view.d.ts +2 -2
- package/dist/sponsorship-view.js +2 -62
- package/dist/story-review.d.cts +6 -6
- package/dist/story-review.d.ts +6 -6
- package/dist/theme-toggle.d.cts +2 -2
- package/dist/theme-toggle.d.ts +2 -2
- package/dist/verified-card.d.cts +3 -3
- package/dist/verified-card.d.ts +3 -3
- package/package.json +40 -3
- package/pets.css +538 -0
- package/tokens/broadsheet.css +46 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React__default from 'react';
|
|
2
|
+
import { PetScope } from './pets-server.cjs';
|
|
3
|
+
import { PetCommunity } from './pets.cjs';
|
|
4
|
+
import '@scalemule/sdk';
|
|
5
|
+
import './sponsorships-CtnBe3S8.cjs';
|
|
6
|
+
|
|
7
|
+
/** Server rendering adapters. Publications keep their host routing, identity and credentials. */
|
|
8
|
+
|
|
9
|
+
type PetPageQuery = {
|
|
10
|
+
community?: string;
|
|
11
|
+
after?: string;
|
|
12
|
+
};
|
|
13
|
+
declare function PublicationPetsSection({ scope, query, region, canonicalOrigin, defaultCommunity, walksUrl, cleanCaption, }: {
|
|
14
|
+
scope: PetScope;
|
|
15
|
+
query: PetPageQuery;
|
|
16
|
+
region: string;
|
|
17
|
+
canonicalOrigin: string;
|
|
18
|
+
defaultCommunity?: string;
|
|
19
|
+
walksUrl?: string;
|
|
20
|
+
cleanCaption?: (caption: string) => string;
|
|
21
|
+
}): Promise<React__default.JSX.Element>;
|
|
22
|
+
declare function PublicationPetReferences({ scope, metadata, canonicalOrigin, communities, cleanCaption, }: {
|
|
23
|
+
scope: PetScope;
|
|
24
|
+
metadata: unknown;
|
|
25
|
+
canonicalOrigin: string;
|
|
26
|
+
communities?: PetCommunity[];
|
|
27
|
+
cleanCaption?: (value: string) => string;
|
|
28
|
+
}): Promise<React__default.JSX.Element | null>;
|
|
29
|
+
/** Extends the existing publication calendar with reviewed source references. */
|
|
30
|
+
declare function PublicationPetEvents({ scope, canonicalOrigin, cleanCaption, }: {
|
|
31
|
+
scope: PetScope;
|
|
32
|
+
canonicalOrigin: string;
|
|
33
|
+
cleanCaption?: (value: string) => string;
|
|
34
|
+
}): Promise<React__default.JSX.Element | null>;
|
|
35
|
+
|
|
36
|
+
export { type PetPageQuery, PublicationPetEvents, PublicationPetReferences, PublicationPetsSection };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React__default from 'react';
|
|
2
|
+
import { PetScope } from './pets-server.js';
|
|
3
|
+
import { PetCommunity } from './pets.js';
|
|
4
|
+
import '@scalemule/sdk';
|
|
5
|
+
import './sponsorships-CtnBe3S8.js';
|
|
6
|
+
|
|
7
|
+
/** Server rendering adapters. Publications keep their host routing, identity and credentials. */
|
|
8
|
+
|
|
9
|
+
type PetPageQuery = {
|
|
10
|
+
community?: string;
|
|
11
|
+
after?: string;
|
|
12
|
+
};
|
|
13
|
+
declare function PublicationPetsSection({ scope, query, region, canonicalOrigin, defaultCommunity, walksUrl, cleanCaption, }: {
|
|
14
|
+
scope: PetScope;
|
|
15
|
+
query: PetPageQuery;
|
|
16
|
+
region: string;
|
|
17
|
+
canonicalOrigin: string;
|
|
18
|
+
defaultCommunity?: string;
|
|
19
|
+
walksUrl?: string;
|
|
20
|
+
cleanCaption?: (caption: string) => string;
|
|
21
|
+
}): Promise<React__default.JSX.Element>;
|
|
22
|
+
declare function PublicationPetReferences({ scope, metadata, canonicalOrigin, communities, cleanCaption, }: {
|
|
23
|
+
scope: PetScope;
|
|
24
|
+
metadata: unknown;
|
|
25
|
+
canonicalOrigin: string;
|
|
26
|
+
communities?: PetCommunity[];
|
|
27
|
+
cleanCaption?: (value: string) => string;
|
|
28
|
+
}): Promise<React__default.JSX.Element | null>;
|
|
29
|
+
/** Extends the existing publication calendar with reviewed source references. */
|
|
30
|
+
declare function PublicationPetEvents({ scope, canonicalOrigin, cleanCaption, }: {
|
|
31
|
+
scope: PetScope;
|
|
32
|
+
canonicalOrigin: string;
|
|
33
|
+
cleanCaption?: (value: string) => string;
|
|
34
|
+
}): Promise<React__default.JSX.Element | null>;
|
|
35
|
+
|
|
36
|
+
export { type PetPageQuery, PublicationPetEvents, PublicationPetReferences, PublicationPetsSection };
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { PetsSection, PawOfTheWeekCard, AdoptionListingCard, PetEventCard, PetCelebrationCard, LostPetNotice, PetProfileCard } from './chunk-TTLM7YOH.js';
|
|
2
|
+
import './chunk-4JRWZXAP.js';
|
|
3
|
+
import { petRequest, petAcknowledgments, resolvePetReferences } from './chunk-ZXH3O537.js';
|
|
4
|
+
import './chunk-66GI2IX6.js';
|
|
5
|
+
import { resolvePetCommunity } from './chunk-MBBPEFDA.js';
|
|
6
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
7
|
+
import { createElement } from 'react';
|
|
8
|
+
|
|
9
|
+
async function PublicationPetsSection({
|
|
10
|
+
scope,
|
|
11
|
+
query,
|
|
12
|
+
region,
|
|
13
|
+
canonicalOrigin,
|
|
14
|
+
defaultCommunity,
|
|
15
|
+
walksUrl,
|
|
16
|
+
cleanCaption = (v) => v
|
|
17
|
+
}) {
|
|
18
|
+
const root = `/v1/knowledge/community-maps/collections/${scope.collectionSlug}`;
|
|
19
|
+
const mediaUrl = (id, photo) => `/api/pets/${id}/media/${photo}`;
|
|
20
|
+
try {
|
|
21
|
+
const collection = await petRequest(scope, root);
|
|
22
|
+
const home = defaultCommunity || collection.areas.find(
|
|
23
|
+
(area) => area.name.toLocaleLowerCase() === region.toLocaleLowerCase()
|
|
24
|
+
)?.id;
|
|
25
|
+
const chosen = resolvePetCommunity(
|
|
26
|
+
collection.areas,
|
|
27
|
+
query.community,
|
|
28
|
+
void 0,
|
|
29
|
+
home
|
|
30
|
+
);
|
|
31
|
+
const filter = new URLSearchParams();
|
|
32
|
+
if (chosen) filter.set("area_id", chosen.id);
|
|
33
|
+
if (query.after) filter.set("after", query.after);
|
|
34
|
+
const date = new Intl.DateTimeFormat("en-CA", {
|
|
35
|
+
timeZone: "America/Los_Angeles",
|
|
36
|
+
year: "numeric",
|
|
37
|
+
month: "2-digit",
|
|
38
|
+
day: "2-digit"
|
|
39
|
+
}).format(/* @__PURE__ */ new Date());
|
|
40
|
+
const monday = /* @__PURE__ */ new Date(date + "T12:00:00Z");
|
|
41
|
+
monday.setUTCDate(monday.getUTCDate() - (monday.getUTCDay() + 6) % 7);
|
|
42
|
+
const weekly = new URLSearchParams({
|
|
43
|
+
kind: "spotlight",
|
|
44
|
+
week: monday.toISOString().split("T")[0]
|
|
45
|
+
});
|
|
46
|
+
if (chosen) weekly.set("area_id", chosen.id);
|
|
47
|
+
const [feeds, spotlights] = await Promise.all([
|
|
48
|
+
Promise.all(
|
|
49
|
+
["neighbors", "lost_found", "celebration", "adoption"].map((kind) => {
|
|
50
|
+
const q = new URLSearchParams(filter);
|
|
51
|
+
q.set("kind", kind);
|
|
52
|
+
q.set("limit", "6");
|
|
53
|
+
return petRequest(
|
|
54
|
+
scope,
|
|
55
|
+
root + "/pets?" + q
|
|
56
|
+
);
|
|
57
|
+
})
|
|
58
|
+
),
|
|
59
|
+
petRequest(scope, root + "/pets?" + weekly)
|
|
60
|
+
]);
|
|
61
|
+
const clean = (r) => ({
|
|
62
|
+
...r,
|
|
63
|
+
media: r.media.map((m) => ({
|
|
64
|
+
...m,
|
|
65
|
+
caption: cleanCaption(m.caption),
|
|
66
|
+
attribution: cleanCaption(m.attribution)
|
|
67
|
+
}))
|
|
68
|
+
});
|
|
69
|
+
const local = chosen ? "?community=" + encodeURIComponent(chosen.slug) : "";
|
|
70
|
+
return /* @__PURE__ */ jsx(
|
|
71
|
+
PetsSection,
|
|
72
|
+
{
|
|
73
|
+
basePath: "/pets",
|
|
74
|
+
regionLabel: region,
|
|
75
|
+
resourceBasePath: canonicalOrigin + "/pets",
|
|
76
|
+
communities: collection.areas,
|
|
77
|
+
community: chosen,
|
|
78
|
+
acknowledgments: await petAcknowledgments(scope, chosen?.slug),
|
|
79
|
+
resources: feeds.flatMap((feed) => feed.items).map(clean),
|
|
80
|
+
spotlight: spotlights.items[0] && clean(spotlights.items[0]),
|
|
81
|
+
mediaUrl,
|
|
82
|
+
contributeUrl: canonicalOrigin + "/submit" + local,
|
|
83
|
+
placesUrl: canonicalOrigin + "/places" + local,
|
|
84
|
+
walksUrl,
|
|
85
|
+
browseBasePath: canonicalOrigin + "/browse",
|
|
86
|
+
eventsUrl: canonicalOrigin + "/events"
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
} catch {
|
|
90
|
+
return /* @__PURE__ */ jsx(
|
|
91
|
+
PetsSection,
|
|
92
|
+
{
|
|
93
|
+
communities: [],
|
|
94
|
+
resources: [],
|
|
95
|
+
mediaUrl,
|
|
96
|
+
regionLabel: region,
|
|
97
|
+
unavailable: true
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function PublicationPetReferences({
|
|
103
|
+
scope,
|
|
104
|
+
metadata,
|
|
105
|
+
canonicalOrigin,
|
|
106
|
+
communities,
|
|
107
|
+
cleanCaption = (v) => v
|
|
108
|
+
}) {
|
|
109
|
+
let resources;
|
|
110
|
+
try {
|
|
111
|
+
resources = await resolvePetReferences(scope, metadata);
|
|
112
|
+
} catch {
|
|
113
|
+
return /* @__PURE__ */ jsx("p", { children: "Linked pet resources are temporarily unavailable." });
|
|
114
|
+
}
|
|
115
|
+
if (!resources.length) return null;
|
|
116
|
+
return /* @__PURE__ */ jsx("section", { className: "paw", "aria-label": "Related community pet resources", children: resources.map((resource) => {
|
|
117
|
+
const props = {
|
|
118
|
+
resource: {
|
|
119
|
+
...resource,
|
|
120
|
+
media: resource.media.map((m) => ({
|
|
121
|
+
...m,
|
|
122
|
+
caption: cleanCaption(m.caption),
|
|
123
|
+
attribution: cleanCaption(m.attribution)
|
|
124
|
+
}))
|
|
125
|
+
},
|
|
126
|
+
community: communities?.find((c) => c.id === resource.area_id)?.name || "Our community",
|
|
127
|
+
href: `${canonicalOrigin}/pets/${resource.id}`,
|
|
128
|
+
mediaUrl: (id, photo) => `/api/pets/${id}/media/${photo}`
|
|
129
|
+
};
|
|
130
|
+
const Card = resource.kind === "spotlight" ? PawOfTheWeekCard : resource.kind === "adoption" ? AdoptionListingCard : resource.kind === "event" ? PetEventCard : resource.kind === "celebration" ? PetCelebrationCard : ["lost", "found"].includes(resource.kind) ? LostPetNotice : PetProfileCard;
|
|
131
|
+
return /* @__PURE__ */ createElement(Card, { ...props, key: resource.id });
|
|
132
|
+
}) });
|
|
133
|
+
}
|
|
134
|
+
async function PublicationPetEvents({
|
|
135
|
+
scope,
|
|
136
|
+
canonicalOrigin,
|
|
137
|
+
cleanCaption = (value) => value
|
|
138
|
+
}) {
|
|
139
|
+
try {
|
|
140
|
+
const query = new URLSearchParams({
|
|
141
|
+
kind: "event",
|
|
142
|
+
limit: "6",
|
|
143
|
+
starts_after: (/* @__PURE__ */ new Date()).toISOString(),
|
|
144
|
+
starts_before: new Date(Date.now() + 7 * 864e5).toISOString()
|
|
145
|
+
});
|
|
146
|
+
const { items } = await petRequest(
|
|
147
|
+
scope,
|
|
148
|
+
`/v1/knowledge/community-maps/collections/${scope.collectionSlug}/pets?${query}`
|
|
149
|
+
);
|
|
150
|
+
if (!items.length) return null;
|
|
151
|
+
return /* @__PURE__ */ jsxs("section", { className: "paw paw-section", "aria-label": "Pet events this week", children: [
|
|
152
|
+
/* @__PURE__ */ jsx("h2", { children: "This week for pets and their people" }),
|
|
153
|
+
/* @__PURE__ */ jsx("div", { className: "paw-gallery", children: items.map((resource) => /* @__PURE__ */ jsx(
|
|
154
|
+
PetEventCard,
|
|
155
|
+
{
|
|
156
|
+
resource: {
|
|
157
|
+
...resource,
|
|
158
|
+
media: resource.media.map((photo) => ({
|
|
159
|
+
...photo,
|
|
160
|
+
caption: cleanCaption(photo.caption),
|
|
161
|
+
attribution: cleanCaption(photo.attribution)
|
|
162
|
+
}))
|
|
163
|
+
},
|
|
164
|
+
community: "Our community",
|
|
165
|
+
href: `${canonicalOrigin}/pets/${resource.id}`,
|
|
166
|
+
mediaUrl: (id, media) => `/api/pets/${id}/media/${media}`
|
|
167
|
+
},
|
|
168
|
+
resource.id
|
|
169
|
+
)) }),
|
|
170
|
+
/* @__PURE__ */ jsx("a", { href: canonicalOrigin + "/events", children: "Browse the pet community calendar" })
|
|
171
|
+
] });
|
|
172
|
+
} catch {
|
|
173
|
+
return /* @__PURE__ */ jsx("p", { role: "status", children: "Pet event listings are temporarily unavailable." });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export { PublicationPetEvents, PublicationPetReferences, PublicationPetsSection };
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var sdk = require('@scalemule/sdk');
|
|
4
|
+
|
|
5
|
+
// src/pets-server.ts
|
|
6
|
+
|
|
7
|
+
// src/pets.ts
|
|
8
|
+
var PET_KINDS = [
|
|
9
|
+
"profile",
|
|
10
|
+
"spotlight",
|
|
11
|
+
"photo",
|
|
12
|
+
"celebration",
|
|
13
|
+
"lost",
|
|
14
|
+
"found",
|
|
15
|
+
"adoption",
|
|
16
|
+
"event"
|
|
17
|
+
];
|
|
18
|
+
var PET_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
19
|
+
var PET_SLUG = /^[a-z0-9][a-z0-9-]{0,190}$/;
|
|
20
|
+
function petReferences(metadata) {
|
|
21
|
+
const entries = metadata && typeof metadata === "object" ? metadata.pet_resources : null;
|
|
22
|
+
if (!Array.isArray(entries)) return [];
|
|
23
|
+
return entries.slice(0, 12).flatMap((v) => {
|
|
24
|
+
if (!v || typeof v !== "object") return [];
|
|
25
|
+
const r = v;
|
|
26
|
+
return typeof r.collection_slug === "string" && PET_SLUG.test(r.collection_slug) && typeof r.id === "string" && PET_UUID.test(r.id) && PET_KINDS.includes(r.kind) ? [
|
|
27
|
+
{
|
|
28
|
+
collection_slug: r.collection_slug,
|
|
29
|
+
id: r.id,
|
|
30
|
+
kind: r.kind
|
|
31
|
+
}
|
|
32
|
+
] : [];
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/pets-server.ts
|
|
37
|
+
var PetRequestError = class extends Error {
|
|
38
|
+
constructor(status, message) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.status = status;
|
|
41
|
+
}
|
|
42
|
+
status;
|
|
43
|
+
};
|
|
44
|
+
var petPrivateHeaders = {
|
|
45
|
+
"cache-control": "private, no-store",
|
|
46
|
+
"x-robots-tag": "noindex, nofollow",
|
|
47
|
+
"referrer-policy": "no-referrer",
|
|
48
|
+
"x-content-type-options": "nosniff"
|
|
49
|
+
};
|
|
50
|
+
async function bounded(request, max) {
|
|
51
|
+
if (Number(request.headers.get("content-length")) > max)
|
|
52
|
+
throw new PetRequestError(413, "The submission is too large.");
|
|
53
|
+
const reader = request.body?.getReader();
|
|
54
|
+
if (!reader) return new Uint8Array();
|
|
55
|
+
const parts = [];
|
|
56
|
+
let size = 0;
|
|
57
|
+
try {
|
|
58
|
+
for (; ; ) {
|
|
59
|
+
const { done, value } = await reader.read();
|
|
60
|
+
if (done) break;
|
|
61
|
+
size += value.length;
|
|
62
|
+
if (size > max) {
|
|
63
|
+
await reader.cancel();
|
|
64
|
+
throw new PetRequestError(413, "The submission is too large.");
|
|
65
|
+
}
|
|
66
|
+
parts.push(value);
|
|
67
|
+
}
|
|
68
|
+
} finally {
|
|
69
|
+
reader.releaseLock();
|
|
70
|
+
}
|
|
71
|
+
return Buffer.concat(parts);
|
|
72
|
+
}
|
|
73
|
+
function config(scope) {
|
|
74
|
+
const url = new URL(scope.gatewayUrl);
|
|
75
|
+
if (!scope.apiKey || !PET_SLUG.test(scope.collectionSlug) || url.username || url.password || url.protocol !== "https:" && !(url.protocol === "http:" && ["localhost", "127.0.0.1"].includes(url.hostname)))
|
|
76
|
+
throw new PetRequestError(503, "The pet community is not available yet.");
|
|
77
|
+
return url.origin;
|
|
78
|
+
}
|
|
79
|
+
async function petRequest(scope, path, init = {}, fetcher = fetch) {
|
|
80
|
+
const gateway = config(scope);
|
|
81
|
+
if (!path.startsWith("/v1/") || path.includes("..") || /[\\\r\n]/.test(path))
|
|
82
|
+
throw new Error("Invalid platform path");
|
|
83
|
+
const response = await fetcher(gateway + path, {
|
|
84
|
+
method: init.method || "GET",
|
|
85
|
+
headers: {
|
|
86
|
+
...sdk.buildClientContextHeaders(scope.clientContext),
|
|
87
|
+
"x-api-key": scope.apiKey,
|
|
88
|
+
"content-type": "application/json",
|
|
89
|
+
...scope.accessToken ? { authorization: `Bearer ${scope.accessToken}` } : {}
|
|
90
|
+
},
|
|
91
|
+
body: init.body === void 0 ? void 0 : JSON.stringify(init.body),
|
|
92
|
+
cache: "no-store",
|
|
93
|
+
redirect: "error",
|
|
94
|
+
signal: AbortSignal.timeout(15e3)
|
|
95
|
+
});
|
|
96
|
+
if (!response.ok)
|
|
97
|
+
throw new PetRequestError(
|
|
98
|
+
response.status,
|
|
99
|
+
response.status === 401 ? "Please sign in to continue." : response.status === 404 ? "This pet resource is not available." : response.status === 409 ? "This contribution changed. Reload before saving." : response.status === 429 ? "Please wait before trying again." : "We couldn\u2019t complete that request. Please try again."
|
|
100
|
+
);
|
|
101
|
+
const json = await response.json();
|
|
102
|
+
return json?.data ?? json;
|
|
103
|
+
}
|
|
104
|
+
function createPublicationPetsHandler(options) {
|
|
105
|
+
const fetcher = options.fetch || fetch;
|
|
106
|
+
return async function handler(request, path) {
|
|
107
|
+
const json = (data, status = 200) => Response.json(data, { status, headers: petPrivateHeaders });
|
|
108
|
+
try {
|
|
109
|
+
const scope = await options.resolve(request), gateway = config(scope), method = request.method;
|
|
110
|
+
const root = `/v1/knowledge/community-maps/collections/${scope.collectionSlug}`;
|
|
111
|
+
const id = path[0] && PET_UUID.test(path[0]);
|
|
112
|
+
const isMedia = id && path.length === 3 && path[1] === "media" && PET_UUID.test(path[2]);
|
|
113
|
+
const upload = path.length === 3 && path[0] === "submissions" && PET_UUID.test(path[1]) && path[2] === "media";
|
|
114
|
+
const ownMedia = path.length === 4 && path[0] === "submissions" && PET_UUID.test(path[1]) && path[2] === "media" && PET_UUID.test(path[3]);
|
|
115
|
+
const own = path[0] === "mine" && (path.length === 1 || path.length === 2 && PET_UUID.test(path[1]));
|
|
116
|
+
const preferences = path.length === 1 && path[0] === "preferences";
|
|
117
|
+
const reaction = id && path.length === 2 && path[1] === "reaction";
|
|
118
|
+
const report = id && path.length === 2 && path[1] === "reports";
|
|
119
|
+
const publicRead = method === "GET" && (!path.length || id && path.length === 1 || isMedia || path.length === 1 && path[0] === "community");
|
|
120
|
+
const read = publicRead || method === "GET" && (own || ownMedia || preferences || reaction);
|
|
121
|
+
const write = method === "POST" && (!path.length || upload || report || id && path.length === 2 && path[1] === "reunited") || method === "PUT" && (preferences || reaction) || ["PUT", "DELETE"].includes(method) && id && path.length === 1;
|
|
122
|
+
if (!read && !write) return json({ error: "Route not found" }, 404);
|
|
123
|
+
if (write && (!scope.writesEnabled || request.headers.get("origin") !== new URL(scope.origin).origin))
|
|
124
|
+
return json(
|
|
125
|
+
{ error: "Continue from the pet community\u2019s official website." },
|
|
126
|
+
403
|
|
127
|
+
);
|
|
128
|
+
if ((write || own || ownMedia || preferences || reaction) && !scope.accessToken)
|
|
129
|
+
return json({ error: "Sign in to continue." }, 401);
|
|
130
|
+
let endpoint = root + (path[0] === "community" ? "" : upload || ownMedia ? "/" + path.join("/") : "/pets" + (path.length ? "/" + path.join("/") : ""));
|
|
131
|
+
const query = new URLSearchParams();
|
|
132
|
+
if (method === "GET" && !path.length)
|
|
133
|
+
for (const key of [
|
|
134
|
+
"area_id",
|
|
135
|
+
"kind",
|
|
136
|
+
"after",
|
|
137
|
+
"week",
|
|
138
|
+
"limit",
|
|
139
|
+
"category",
|
|
140
|
+
"species",
|
|
141
|
+
"event_type",
|
|
142
|
+
"cost",
|
|
143
|
+
"starts_after",
|
|
144
|
+
"starts_before"
|
|
145
|
+
]) {
|
|
146
|
+
const value = new URL(request.url).searchParams.get(key);
|
|
147
|
+
if (value) query.set(key, value);
|
|
148
|
+
}
|
|
149
|
+
if (own) {
|
|
150
|
+
const after = new URL(request.url).searchParams.get("after");
|
|
151
|
+
if (after && PET_UUID.test(after)) query.set("after", after);
|
|
152
|
+
}
|
|
153
|
+
if (query.size) endpoint += "?" + query;
|
|
154
|
+
if (isMedia) {
|
|
155
|
+
const imageQuery = new URLSearchParams();
|
|
156
|
+
const sourceQuery = new URL(request.url).searchParams;
|
|
157
|
+
if (["640", "1280"].includes(sourceQuery.get("width") || ""))
|
|
158
|
+
imageQuery.set("width", sourceQuery.get("width"));
|
|
159
|
+
if (["jpeg", "webp"].includes(sourceQuery.get("format") || ""))
|
|
160
|
+
imageQuery.set("format", sourceQuery.get("format"));
|
|
161
|
+
if (imageQuery.size) endpoint += "?" + imageQuery;
|
|
162
|
+
}
|
|
163
|
+
if (isMedia || upload || ownMedia) {
|
|
164
|
+
let body2;
|
|
165
|
+
const type = request.headers.get("content-type") || "";
|
|
166
|
+
if (upload) {
|
|
167
|
+
if (!type.startsWith("multipart/form-data;"))
|
|
168
|
+
throw new PetRequestError(415, "Choose a photograph.");
|
|
169
|
+
body2 = await bounded(request, 9 * 1024 * 1024);
|
|
170
|
+
}
|
|
171
|
+
const response = await fetcher(gateway + endpoint, {
|
|
172
|
+
method,
|
|
173
|
+
headers: {
|
|
174
|
+
...sdk.buildClientContextHeaders(scope.clientContext),
|
|
175
|
+
"x-api-key": scope.apiKey,
|
|
176
|
+
...scope.accessToken ? { authorization: `Bearer ${scope.accessToken}` } : {},
|
|
177
|
+
...upload ? { "content-type": type } : {}
|
|
178
|
+
},
|
|
179
|
+
body: body2,
|
|
180
|
+
cache: "no-store",
|
|
181
|
+
redirect: "error",
|
|
182
|
+
signal: AbortSignal.timeout(45e3)
|
|
183
|
+
});
|
|
184
|
+
if (!response.ok) {
|
|
185
|
+
await response.body?.cancel();
|
|
186
|
+
return json(
|
|
187
|
+
{
|
|
188
|
+
error: "The photograph is unavailable. Check its size and try again."
|
|
189
|
+
},
|
|
190
|
+
response.status >= 500 ? 503 : response.status
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
if (upload) return json(await response.json());
|
|
194
|
+
const imageType = response.headers.get("content-type")?.split(";")[0] || "";
|
|
195
|
+
if (!["image/png", "image/jpeg", "image/webp"].includes(imageType)) {
|
|
196
|
+
await response.body?.cancel();
|
|
197
|
+
return json({ error: "Photograph unavailable" }, 404);
|
|
198
|
+
}
|
|
199
|
+
return new Response(response.body, {
|
|
200
|
+
headers: { ...petPrivateHeaders, "content-type": imageType }
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
let body;
|
|
204
|
+
if (write) {
|
|
205
|
+
if (!request.headers.get("content-type")?.startsWith("application/json"))
|
|
206
|
+
throw new PetRequestError(415, "Use the pet contribution form.");
|
|
207
|
+
try {
|
|
208
|
+
body = JSON.parse(
|
|
209
|
+
new TextDecoder().decode(await bounded(request, 32768))
|
|
210
|
+
);
|
|
211
|
+
} catch (error) {
|
|
212
|
+
if (error instanceof PetRequestError) throw error;
|
|
213
|
+
throw new PetRequestError(400, "Check the contribution fields.");
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return json({
|
|
217
|
+
data: await petRequest(scope, endpoint, { method, body }, fetcher)
|
|
218
|
+
});
|
|
219
|
+
} catch (error) {
|
|
220
|
+
return json(
|
|
221
|
+
{
|
|
222
|
+
error: error instanceof PetRequestError ? error.message : "The pet community is temporarily unavailable."
|
|
223
|
+
},
|
|
224
|
+
error instanceof PetRequestError ? error.status : 503
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
async function resolvePetReferences(scope, metadata, fetcher = fetch) {
|
|
230
|
+
const references = petReferences(metadata).filter(
|
|
231
|
+
(ref) => ref.collection_slug === scope.collectionSlug
|
|
232
|
+
);
|
|
233
|
+
const results = await Promise.all(
|
|
234
|
+
references.map(async (ref) => {
|
|
235
|
+
try {
|
|
236
|
+
const resource = await petRequest(
|
|
237
|
+
{ ...scope, accessToken: void 0 },
|
|
238
|
+
`/v1/knowledge/community-maps/collections/${ref.collection_slug}/pets/${ref.id}`,
|
|
239
|
+
{},
|
|
240
|
+
fetcher
|
|
241
|
+
);
|
|
242
|
+
return resource.kind === ref.kind ? resource : null;
|
|
243
|
+
} catch (error) {
|
|
244
|
+
if (error instanceof PetRequestError && [403, 404].includes(error.status))
|
|
245
|
+
return null;
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
);
|
|
250
|
+
return results.filter(
|
|
251
|
+
(resource) => resource !== null
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
async function petAcknowledgments(scope, community, productPath = "/pets", fetcher = fetch) {
|
|
255
|
+
if (!scope.publicationId || !PET_UUID.test(scope.publicationId)) return [];
|
|
256
|
+
try {
|
|
257
|
+
const groups = await Promise.all(
|
|
258
|
+
["community_partner", "community_guide"].map(
|
|
259
|
+
async (placement) => {
|
|
260
|
+
const query = new URLSearchParams({
|
|
261
|
+
placement_type: placement,
|
|
262
|
+
product_path: productPath
|
|
263
|
+
});
|
|
264
|
+
if (community) query.set("community", community);
|
|
265
|
+
const response = await petRequest(
|
|
266
|
+
{ ...scope, accessToken: void 0 },
|
|
267
|
+
`/v1/blog/publications/${scope.publicationId}/sponsorships/delivery?${query}`,
|
|
268
|
+
{},
|
|
269
|
+
fetcher
|
|
270
|
+
);
|
|
271
|
+
return response.items.filter(
|
|
272
|
+
(item) => item.publication_id === scope.publicationId && item.placement_type === placement
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
)
|
|
276
|
+
);
|
|
277
|
+
return groups.flat().slice(0, 2);
|
|
278
|
+
} catch {
|
|
279
|
+
console.warn("paw.sponsorship_delivery_unavailable");
|
|
280
|
+
return [];
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
exports.PetRequestError = PetRequestError;
|
|
285
|
+
exports.createPublicationPetsHandler = createPublicationPetsHandler;
|
|
286
|
+
exports.petAcknowledgments = petAcknowledgments;
|
|
287
|
+
exports.petPrivateHeaders = petPrivateHeaders;
|
|
288
|
+
exports.petRequest = petRequest;
|
|
289
|
+
exports.resolvePetReferences = resolvePetReferences;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ClientContext } from '@scalemule/sdk';
|
|
2
|
+
import { P as PublicAcknowledgment } from './sponsorships-CtnBe3S8.cjs';
|
|
3
|
+
import { PetResource } from './pets.cjs';
|
|
4
|
+
|
|
5
|
+
/** Server-only, publication-scoped proxy. Never import credentials into a client component. */
|
|
6
|
+
|
|
7
|
+
type PetScope = {
|
|
8
|
+
apiKey: string;
|
|
9
|
+
gatewayUrl: string;
|
|
10
|
+
origin: string;
|
|
11
|
+
collectionSlug: string;
|
|
12
|
+
publicationId?: string;
|
|
13
|
+
accessToken?: string;
|
|
14
|
+
clientContext: ClientContext;
|
|
15
|
+
writesEnabled?: boolean;
|
|
16
|
+
};
|
|
17
|
+
declare class PetRequestError extends Error {
|
|
18
|
+
status: number;
|
|
19
|
+
constructor(status: number, message: string);
|
|
20
|
+
}
|
|
21
|
+
declare const petPrivateHeaders: {
|
|
22
|
+
"cache-control": string;
|
|
23
|
+
"x-robots-tag": string;
|
|
24
|
+
"referrer-policy": string;
|
|
25
|
+
"x-content-type-options": string;
|
|
26
|
+
};
|
|
27
|
+
declare function petRequest<T>(scope: PetScope, path: string, init?: {
|
|
28
|
+
method?: string;
|
|
29
|
+
body?: unknown;
|
|
30
|
+
}, fetcher?: typeof fetch): Promise<T>;
|
|
31
|
+
declare function createPublicationPetsHandler(options: {
|
|
32
|
+
resolve(request: Request): Promise<PetScope>;
|
|
33
|
+
fetch?: typeof fetch;
|
|
34
|
+
}): (request: Request, path: string[]) => Promise<Response>;
|
|
35
|
+
/** Resolve article references at render time through the publication's accepted view.
|
|
36
|
+
* Withdrawing a resource removes its embedded card; articles never cache private snapshots. */
|
|
37
|
+
declare function resolvePetReferences(scope: PetScope, metadata: unknown, fetcher?: typeof fetch): Promise<PetResource[]>;
|
|
38
|
+
/** The existing campaign service owns approval, agreements, geography and reporting.
|
|
39
|
+
* No campaign is inferred from a logo, partner prospect or submitted pet content. */
|
|
40
|
+
declare function petAcknowledgments(scope: PetScope, community?: string, productPath?: string, fetcher?: typeof fetch): Promise<PublicAcknowledgment[]>;
|
|
41
|
+
|
|
42
|
+
export { PetRequestError, type PetScope, createPublicationPetsHandler, petAcknowledgments, petPrivateHeaders, petRequest, resolvePetReferences };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ClientContext } from '@scalemule/sdk';
|
|
2
|
+
import { P as PublicAcknowledgment } from './sponsorships-CtnBe3S8.js';
|
|
3
|
+
import { PetResource } from './pets.js';
|
|
4
|
+
|
|
5
|
+
/** Server-only, publication-scoped proxy. Never import credentials into a client component. */
|
|
6
|
+
|
|
7
|
+
type PetScope = {
|
|
8
|
+
apiKey: string;
|
|
9
|
+
gatewayUrl: string;
|
|
10
|
+
origin: string;
|
|
11
|
+
collectionSlug: string;
|
|
12
|
+
publicationId?: string;
|
|
13
|
+
accessToken?: string;
|
|
14
|
+
clientContext: ClientContext;
|
|
15
|
+
writesEnabled?: boolean;
|
|
16
|
+
};
|
|
17
|
+
declare class PetRequestError extends Error {
|
|
18
|
+
status: number;
|
|
19
|
+
constructor(status: number, message: string);
|
|
20
|
+
}
|
|
21
|
+
declare const petPrivateHeaders: {
|
|
22
|
+
"cache-control": string;
|
|
23
|
+
"x-robots-tag": string;
|
|
24
|
+
"referrer-policy": string;
|
|
25
|
+
"x-content-type-options": string;
|
|
26
|
+
};
|
|
27
|
+
declare function petRequest<T>(scope: PetScope, path: string, init?: {
|
|
28
|
+
method?: string;
|
|
29
|
+
body?: unknown;
|
|
30
|
+
}, fetcher?: typeof fetch): Promise<T>;
|
|
31
|
+
declare function createPublicationPetsHandler(options: {
|
|
32
|
+
resolve(request: Request): Promise<PetScope>;
|
|
33
|
+
fetch?: typeof fetch;
|
|
34
|
+
}): (request: Request, path: string[]) => Promise<Response>;
|
|
35
|
+
/** Resolve article references at render time through the publication's accepted view.
|
|
36
|
+
* Withdrawing a resource removes its embedded card; articles never cache private snapshots. */
|
|
37
|
+
declare function resolvePetReferences(scope: PetScope, metadata: unknown, fetcher?: typeof fetch): Promise<PetResource[]>;
|
|
38
|
+
/** The existing campaign service owns approval, agreements, geography and reporting.
|
|
39
|
+
* No campaign is inferred from a logo, partner prospect or submitted pet content. */
|
|
40
|
+
declare function petAcknowledgments(scope: PetScope, community?: string, productPath?: string, fetcher?: typeof fetch): Promise<PublicAcknowledgment[]>;
|
|
41
|
+
|
|
42
|
+
export { PetRequestError, type PetScope, createPublicationPetsHandler, petAcknowledgments, petPrivateHeaders, petRequest, resolvePetReferences };
|