Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/wacky-files-melt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@nodesecure/contact": minor
"@nodesecure/scanner": minor
"@nodesecure/mama": minor
---

feat: extend contact with free email service flag
36 changes: 33 additions & 3 deletions workspaces/contact/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,18 @@ interface ContactExtractorFromDependenciesResult {
expired: string[];
}

type IlluminatedContact = Contact & {
type IlluminatedContact = ContactWithMetadata & {
dependencies: string[];
}
```

### compareContact(contactA: Partial< Contact >, contactB: Partial< Contact >, options?: CompareOptions): boolean
export type ContactFlag = "free-email-service";

export interface ContactWithMetadata extends Contact {
flags: ContactFlag[];
}

```
### compareContact(contactA: Partial< Contact > | ContactWithMetadata, contactB: Partial< Contact > | ContactWithMetadata, options?: CompareOptions): boolean
Comment thread
clemgbld marked this conversation as resolved.

Compare two contacts and return `true` if they are the same person

Expand Down Expand Up @@ -130,5 +136,29 @@ interface CompareOptions {
}
```

### toContactWithMetadata<T extends Partial<Contact>>(contact: T): T & {flags: ContactFlag[] }

Apply some transformation on a contact such as adding a flag when the contact use a free email service

```ts
import {
toContactWithMetadata
} from "@nodesecure/contact";
import assert from "node:assert";

assert.deepEqual(
toContactWithMetadata({
name:"john doe",
email: "johndoe@gmail.com"
}),
{
name:"john doe",
email: "johndoe@gmail.com",
flags: ["free-email-service"]
}
);
```
```
Comment thread
clemgbld marked this conversation as resolved.
Outdated

## License
MIT
11 changes: 7 additions & 4 deletions workspaces/contact/src/ContactExtractor.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
type IlluminatedContact
} from "./UnlitContact.class.ts";
import { NsResolver } from "./NsResolver.class.ts";
import { toContactWithMetadata } from "./utils/index.ts";
import type { ContactWithMetadata } from "./types.ts";

export type {
IlluminatedContact,
Expand Down Expand Up @@ -100,7 +102,7 @@ export class ContactExtractor {

private addDependencyToUnlitContacts(
unlitContacts: UnlitContact[],
contacts: Contact[],
contacts: ContactWithMetadata[],
packageName: string
) {
for (const unlit of unlitContacts) {
Expand All @@ -127,9 +129,10 @@ export class ContactExtractor {

export function extractMetadataContacts(
metadata: ContactPackageMetaData
): Contact[] {
): ContactWithMetadata[] {
return [
...(metadata.author ? [metadata.author] : []),
...(metadata.maintainers ? metadata.maintainers : [])
...(metadata.author ? [toContactWithMetadata<Contact>(metadata.author)] : []),
...(metadata.maintainers ? metadata.maintainers.map(toContactWithMetadata) : [])
];
}

11 changes: 7 additions & 4 deletions workspaces/contact/src/UnlitContact.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,36 @@ import type { RequireAtLeastOne } from "type-fest";

// Import Internal Dependencies
import * as utils from "./utils/index.ts";
import type { ContactWithMetadata } from "./types.ts";

export type EnforcedContact = RequireAtLeastOne<
Contact,
"name" | "email"
>;

export type IlluminatedContact = EnforcedContact & {
export type EnforcedContactWithMetadata = RequireAtLeastOne<ContactWithMetadata, "name" | "email">;

export type IlluminatedContact = EnforcedContactWithMetadata & {
dependencies: string[];
};

export class UnlitContact {
private illuminated: EnforcedContact;
private illuminated: EnforcedContactWithMetadata;
private extendedName: RegExp | null = null;

public dependencies = new Set<string>();

constructor(
contact: EnforcedContact
) {
this.illuminated = structuredClone(contact);
this.illuminated = structuredClone(utils.toContactWithMetadata(contact));
this.extendedName = typeof contact.name === "string" ?
utils.parseRegExp(contact.name) :
null;
}

compareTo(
contact: Contact
contact: ContactWithMetadata
): boolean {
if (this.extendedName === null) {
return utils.compareContact(this.illuminated, contact);
Expand Down
4 changes: 3 additions & 1 deletion workspaces/contact/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export * from "./ContactExtractor.class.ts";
export {
compareContact
compareContact,
toContactWithMetadata
} from "./utils/index.ts";
export { NsResolver } from "./NsResolver.class.ts";
export { UnlitContact } from "./UnlitContact.class.ts";
export { type ContactWithMetadata } from "./types.ts";
8 changes: 8 additions & 0 deletions workspaces/contact/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Import Third-party Dependencies
import type { Contact } from "@nodesecure/npm-types";

export type ContactFlag = "free-email-service";

export interface ContactWithMetadata extends Contact {
flags: ContactFlag[];
}
1 change: 1 addition & 0 deletions workspaces/contact/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./compareContact.ts";
export * from "./parseRegexp.ts";
export * from "./toContactWithMetadata.ts";
32 changes: 32 additions & 0 deletions workspaces/contact/src/utils/toContactWithMetadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Import Third-party Dependencies
import type { Contact } from "@nodesecure/npm-types";

// Import Internal Dependencies
import type { ContactFlag } from "../types.ts";

// CONSTANS
const kFreeEmailServiceRegex = new RegExp(
"(gmail\\.com|yahoo\\.com|hotmail\\.com|outlook\\.com|live\\.com|" +
"protonmail\\.com|proton\\.me|mail\\.ru|yandex\\.ru|qq\\.com|" +
"163\\.com|aol\\.com|icloud\\.com|zoho\\.com)$"
);

export function toContactWithMetadata<T extends Partial<Contact>>(
contact: T): T & { flags: ContactFlag[]; } {
Comment thread
clemgbld marked this conversation as resolved.
Outdated
if ("flags" in contact) {
return contact as T & { flags: ContactFlag[]; };
}
const flags: ContactFlag[] = [];
if (!(typeof contact.email === "string")) {
return { ...contact, flags: [] as ContactFlag[] };
}

if (kFreeEmailServiceRegex.test(contact.email)) {
flags.push("free-email-service");
}

return {
...contact,
flags
};
}
17 changes: 12 additions & 5 deletions workspaces/contact/test/ContactExtractor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe("ContactExtractor", () => {
assert.deepEqual(
illuminated,
[
{ ...highlighted, dependencies: ["kleur", "mocha"] }
{ ...highlighted, flags: [], dependencies: ["kleur", "mocha"] }
]
);
});
Expand All @@ -85,6 +85,7 @@ describe("ContactExtractor", () => {
[
{
...highlighted,
flags: [],
dependencies: ["kleur", "mocha"]
}
]
Expand Down Expand Up @@ -164,7 +165,7 @@ describe("ContactExtractor", () => {
assert.deepEqual(
illuminated,
[
{ name: "TJ Holowaychuk", dependencies: ["express"] }
{ name: "TJ Holowaychuk", flags: [], dependencies: ["express"] }
]
);
});
Expand All @@ -184,8 +185,12 @@ describe("ContactExtractor", () => {
assert.deepEqual(
illuminated,
[
{ name: "/.*church/", email: "npm@jonchurch.com", dependencies: ["express"] },
{ email: "c@labsector.com", dependencies: ["express"] }
{
name: "/.*church/", email: "npm@jonchurch.com", flags: [], dependencies: ["express"]
},
{
email: "c@labsector.com", flags: [], dependencies: ["express"]
}
]
);
});
Expand Down Expand Up @@ -253,7 +258,7 @@ describe("ContactExtractor", () => {
assert.deepEqual(
illuminated,
[
{ name: "TJ Holowaychuk", dependencies: ["express"] }
{ name: "TJ Holowaychuk", flags: [], dependencies: ["express"] }
]
);
});
Expand All @@ -280,10 +285,12 @@ describe("ContactExtractor", () => {
{
name: "/.*ylman/",
email: "shtylman@gmail.com",
flags: ["free-email-service"],
dependencies: ["express"]
},
{
email: "doug@somethingdoug.com",
flags: [],
dependencies: ["express"]
}
]
Expand Down
62 changes: 62 additions & 0 deletions workspaces/contact/test/utils/toContactWithMetadata.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Import Node.js Dependencies
import assert from "node:assert";
import { describe, test } from "node:test";

// Import Third-party Dependencies
import type { Contact } from "@nodesecure/npm-types";

// Import Internal Dependencies
import { type ContactWithMetadata } from "../../src/types.ts";
import { toContactWithMetadata } from "../../src/utils/index.ts";

describe("toContactWithMetadata", () => {
test("should transform to a contact without the free-domain flag", () => {
const contact: Contact = {
name: "john doe",
email: "john@something.com",
url: "john.com"
};

assert.deepEqual(toContactWithMetadata(contact), {
name: "john doe",
email: "john@something.com",
flags: [],
url: "john.com"
});
});

test("should flag free domain", () => {
const emails = [
"gmail.com", "yahoo.com", "hotmail.com", "outlook.com", "live.com",
"protonmail.com", "proton.me", "mail.ru", "yandex.ru", "qq.com",
"163.com", "aol.com", "icloud.com", "zoho.com"
];

emails.forEach((email) => {
const freeEmailService = `john@${email}`;
const contact: Contact = {
name: "john doe",
email: freeEmailService,
url: "john.com"
};

assert.deepEqual(toContactWithMetadata(contact), {
name: "john doe",
email: freeEmailService,
flags: ["free-email-service"],
url: "john.com"
});
});
});

test("should do nothing when the contact is already a Nsecure one", () => {
const contact: ContactWithMetadata = {
name: "john doe",
email: "johndoe@gmail.com",
url: "john.com",
flags: ["free-email-service"]
};

assert.deepEqual(toContactWithMetadata(contact), contact);
});
});
1 change: 1 addition & 0 deletions workspaces/mama/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"dependencies": {
"@nodesecure/npm-types": "^1.4.0",
"@nodesecure/utils": "^2.3.0",
"@nodesecure/contact": "^3.2.0",
"object-hash": "^3.0.0",
"ssri": "14.0.0"
},
Expand Down
8 changes: 5 additions & 3 deletions workspaces/mama/src/ManifestManager.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import path from "node:path";

// Import Third-party Dependencies
import { parseAuthor } from "@nodesecure/utils";
import { toContactWithMetadata, type ContactWithMetadata } from "@nodesecure/contact";
import type {
PackumentVersion,
PackageJSON,
WorkspacesPackageJSON,
Contact,
AbbreviatedManifestDocument
} from "@nodesecure/npm-types";
import { fromData } from "ssri";
Expand Down Expand Up @@ -174,8 +174,10 @@ export class ManifestManager<
return `${this.document.name}@${this.document.version}`;
}

get author(): Contact | null {
return parseAuthor(this.document.author);
get author(): ContactWithMetadata | null {
const parsedAuthor = parseAuthor(this.document.author);

return parsedAuthor ? toContactWithMetadata(parsedAuthor) : null;
}

get isWorkspace(): boolean {
Expand Down
8 changes: 6 additions & 2 deletions workspaces/mama/test/ManifestManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,8 @@ describe("ManifestManager", () => {
mama.author,
{
name: "John Doe",
email: "john.doe@gmail.com"
email: "john.doe@gmail.com",
flags: ["free-email-service"]
}
);
});
Expand All @@ -477,7 +478,10 @@ describe("ManifestManager", () => {

assert.deepStrictEqual(
mama.author,
packageJSON.author
{ name: "John Doe",
Comment thread
fraxken marked this conversation as resolved.
Outdated
email: "john.doe@gmail.com",
flags: ["free-email-service"]
}
);
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Import Third-party Dependencies
import type { Contact } from "@nodesecure/npm-types";
import type { ContactWithMetadata } from "@nodesecure/contact";

// Import Internal Dependencies
import type {
Expand All @@ -19,7 +19,7 @@ export class Contacts implements ManifestProbeExtractor<ContactsResult> {
#packages: Set<string> = new Set();

#addContact(
user: Contact | null
user: ContactWithMetadata | null
) {
if (!user || !user.email) {
return;
Expand Down
Loading
Loading