Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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

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"]
}
);
```
```

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
```

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@clemgbld it looks like there's still a duplicate closing code fence here


## 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";
31 changes: 31 additions & 0 deletions workspaces/contact/src/utils/toContactWithMetadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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[]; } {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a bit too much in oneline here

Suggested change
export function toContactWithMetadata<T extends Partial<Contact>>(contact: T): T & { flags: ContactFlag[]; } {
export function toContactWithMetadata<T extends Partial<Contact>>(
contact: T
): T & { flags: ContactFlag[]; } {

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
};
}
31 changes: 31 additions & 0 deletions workspaces/contact/src/utils/toNsecureContact.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Import Third-party Dependencies
import type { Contact } from "@nodesecure/npm-types";

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

// CONSTANTS
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 toNsecureContact<T extends Partial<Contact>>(contact: T): T & { flags: ContactFlag[]; } {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same too much on one line

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
Loading
Loading